From 91e6a781647af3880f89d77d06eea5ff6d3ca04e Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sun, 11 May 2025 20:56:51 +0700 Subject: [PATCH 1/9] group proofs --- Cargo.lock | 18 +- .../rs-dpp/src/data_contract/group/v0/mod.rs | 2 +- .../src/state_transition/proof_result.rs | 2 + .../create_genesis_state/test/tokens.rs | 1 + .../batch/tests/token/mint/mod.rs | 2901 +++++++++++++++++ .../batch/tests/token/mod.rs | 2860 +--------------- packages/rs-drive/Cargo.toml | 12 +- .../for_add_group_action/mod.rs | 2 + .../for_add_group_action/v0/mod.rs | 52 +- .../group/insert/add_group_action/mod.rs | 6 + .../group/insert/add_group_action/v0/mod.rs | 310 +- packages/rs-drive/src/drive/group/queries.rs | 127 +- .../prove/prove_state_transition/v0/mod.rs | 133 +- .../batch/token/token_burn_transition.rs | 1 + .../token/token_config_update_transition.rs | 1 + .../token_destroy_frozen_funds_transition.rs | 1 + .../token_emergency_action_transition.rs | 1 + .../batch/token/token_freeze_transition.rs | 1 + .../batch/token/token_mint_transition.rs | 1 + ...et_price_for_direct_purchase_transition.rs | 1 + .../batch/token/token_unfreeze_transition.rs | 1 + .../src/util/batch/drive_op_batch/group.rs | 4 + .../util/batch/drive_op_batch/withdrawals.rs | 2 + .../batch_move_items_in_path_query/mod.rs | 4 + .../batch_move_items_in_path_query/v0/mod.rs | 11 +- .../rs-drive/src/util/grove_operations/mod.rs | 6 +- packages/rs-drive/src/verify/group/mod.rs | 1 + .../verify_action_signers_total_power/mod.rs | 82 + .../v0/mod.rs | 67 + .../src/verify/state_transition/mod.rs | 4 + .../mod.rs | 48 + .../token_transition.rs | 195 ++ .../v0/mod.rs | 81 +- packages/rs-platform-version/Cargo.toml | 2 +- .../drive_verify_method_versions/mod.rs | 1 + .../drive_verify_method_versions/v1.rs | 1 + .../src/version/system_limits/mod.rs | 2 +- 37 files changed, 3809 insertions(+), 3136 deletions(-) create mode 100644 packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs create mode 100644 packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs create mode 100644 packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs create mode 100644 packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs create mode 100644 packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs diff --git a/Cargo.lock b/Cargo.lock index d5c4cd555f7..dc73ab04a69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2169,7 +2169,7 @@ dependencies = [ [[package]] name = "grovedb" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "axum 0.7.5", "bincode", @@ -2201,7 +2201,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "integer-encoding", "intmap", @@ -2211,7 +2211,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "grovedb-costs", "hex", @@ -2223,7 +2223,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "bincode", "bincode_derive", @@ -2247,7 +2247,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "hex", ] @@ -2255,7 +2255,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "blake3", "grovedb-costs", @@ -2274,7 +2274,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "thiserror 2.0.12", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2283,7 +2283,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "hex", "itertools 0.14.0", @@ -2292,7 +2292,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=6d8ce92abe0c888f0a0115a6c88effa04ea87ed3#6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" +source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" dependencies = [ "serde", "serde_with 3.9.0", diff --git a/packages/rs-dpp/src/data_contract/group/v0/mod.rs b/packages/rs-dpp/src/data_contract/group/v0/mod.rs index caa46daaf29..cbb0c3025fe 100644 --- a/packages/rs-dpp/src/data_contract/group/v0/mod.rs +++ b/packages/rs-dpp/src/data_contract/group/v0/mod.rs @@ -88,7 +88,7 @@ impl GroupMethodsV0 for GroupV0 { &self, platform_version: &PlatformVersion, ) -> Result { - let max_group_members = platform_version.system_limits.max_contract_group_size; + let max_group_members = platform_version.system_limits.max_contract_group_size as u32; const GROUP_POWER_LIMIT: GroupMemberPower = u16::MAX as GroupMemberPower; // Check the number of members does not exceed the maximum allowed diff --git a/packages/rs-dpp/src/state_transition/proof_result.rs b/packages/rs-dpp/src/state_transition/proof_result.rs index 98fc6e79f55..b8fb9c6e1dc 100644 --- a/packages/rs-dpp/src/state_transition/proof_result.rs +++ b/packages/rs-dpp/src/state_transition/proof_result.rs @@ -8,6 +8,7 @@ use crate::tokens::token_pricing_schedule::TokenPricingSchedule; use crate::voting::votes::Vote; use platform_value::Identifier; use std::collections::BTreeMap; +use crate::data_contract::group::GroupSumPower; #[derive(Debug, strum::Display, derive_more::TryInto)] pub enum StateTransitionProofResult { @@ -23,6 +24,7 @@ pub enum StateTransitionProofResult { VerifiedBalanceTransfer(PartialIdentity, PartialIdentity), //from/to VerifiedDocuments(BTreeMap>), VerifiedTokenActionWithDocument(Document), + VerifiedTokenGroupActionWithDocument(GroupSumPower, Option), VerifiedMasternodeVote(Vote), VerifiedNextDistribution(Vote), } diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs index a7ca5d32ffb..8c36df1c828 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs @@ -95,6 +95,7 @@ impl Platform { DATA_CONTRACT_ID, 2, Some(group_action), + false, action_id, IDENTITY_ID_1, 1, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs new file mode 100644 index 00000000000..ce79e4a7d06 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -0,0 +1,2901 @@ +use super::*; +mod token_mint_tests { + use super::*; + + mod token_mint_tests_normal_scenarios { + use super::*; + + #[test] + fn test_token_mint_by_owner_allowed_sending_to_self() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + None::, + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + } + + #[test] + fn test_token_mint_with_public_note() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + None::, + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + Some("this is a public note".to_string()), + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + } + + #[test] + fn test_token_mint_by_owner_can_not_mint_past_max_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply(Some(1000000)); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 2000000, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owner_allowed_sending_to_other() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (receiver, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + None::, + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(receiver.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + receiver.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(1337)); + } + + #[test] + fn test_token_mint_sending_to_non_existing_identity_causes_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let receiver = Identifier::random_with_rng(&mut rng); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + None::, + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(receiver), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::RecipientIdentityDoesNotExistError( + _ + )), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + receiver.to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owner_no_destination_causes_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + None::, + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + None, + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::DestinationIdentityForTokenMintingNotSetError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } + } + + mod token_mint_tests_no_recipient_minting { + use super::*; + + #[test] + fn test_token_mint_by_owned_id_allowed_sending_to_self() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owned_id_allowed_sending_to_other() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (receiver, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(receiver.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + receiver.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owned_id_no_destination_causes_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + None, + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::DestinationIdentityForTokenMintingNotSetError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } + } + + mod token_mint_tests_contract_has_recipient { + use super::*; + + #[test] + fn test_token_mint_by_owned_id_allowed_sending_to_self() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + token_configuration + .distribution_rules_mut() + .set_new_tokens_destination_identity(Some(identity.id())); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owned_id_allowed_sending_to_other() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (receiver, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + token_configuration + .distribution_rules_mut() + .set_new_tokens_destination_identity(Some(identity.id())); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(receiver.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + receiver.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owned_id_no_set_destination_should_use_contracts() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration + .distribution_rules_mut() + .set_minting_allow_choosing_destination(false); + token_configuration + .distribution_rules_mut() + .set_new_tokens_destination_identity(Some(identity.id())); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + None, + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + } + } + + mod token_mint_tests_authorization_scenarios { + use std::collections::BTreeMap; + use dpp::data_contract::associated_token::token_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Setters; + use super::*; + use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; + use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; + use dpp::data_contract::change_control_rules::ChangeControlRules; + use dpp::data_contract::group::v0::GroupV0; + use dpp::data_contract::group::Group; + use dpp::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; + use dpp::state_transition::batch_transition::TokenMintTransition; + use dpp::state_transition::proof_result::StateTransitionProofResult; + use drive::drive::Drive; + + #[test] + fn test_token_mint_by_owner_sending_to_self_minting_not_allowed() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::NoOne, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + None, + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owner_sending_to_self_minting_only_allowed_by_group() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 5), (identity_2.id(), 5)].into(), + required_power: 10, + }), + )] + .into(), + ), + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owner_sending_to_self_minting_only_allowed_by_group_enough_member_power( + ) { + // We are using a group, but our member alone has enough power in the group to do the action + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 5), (identity_2.id(), 1)].into(), + required_power: 5, + }), + )] + .into(), + ), + platform_version, + ); + + let documents_batch_create_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + } + + #[test] + fn test_token_mint_by_owner_requires_group_other_member_with_history() { + test_token_mint_by_owner_requires_group_other_member(true); + } + + #[test] + fn test_token_mint_by_owner_requires_group_other_member_no_history() { + test_token_mint_by_owner_requires_group_other_member(false); + } + + fn test_token_mint_by_owner_requires_group_other_member(keeps_minting_history: bool) { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.keeps_history_mut().set_keeps_minting_history(keeps_minting_history); + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Let's verify the proof of the state transition + + let proof = platform.drive.prove_state_transition(&token_mint_transition, None, platform_version).expect("expect to prove state transition"); + + let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof(&token_mint_transition, &BlockInfo::default(), proof.data.as_ref().expect("expected data"), &|_| { + Ok(Some(contract.clone().into())) + }, + platform_version, + ).expect(format!("expect to verify state transition proof {}", hex::encode(&proof.data.expect("expected data"))).as_str()); + + if keeps_minting_history { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithDocument(power, doc) => { + assert_eq!(power, 1); + assert_eq!(doc, None); + } + ); + } else { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenBalance(id, amount) => { + assert_eq!(id, identity.id()); + assert_eq!(amount, 1337); + } + ); + } + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + // Now we need to get the second identity to also sign it + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owner_requires_group_resubmitting_causes_error() { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + // Now we need to get the second identity to also sign it, but we are going to resubmit with first + // This will create an error + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key, + 3, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError( + StateError::GroupActionAlreadySignedByIdentityError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owner_requires_group_other_member_resubmitting_causes_error() { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_3, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [ + (identity.id(), 1), + (identity_2.id(), 1), + (identity_3.id(), 1), + ] + .into(), + required_power: 3, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + // Now we need to get the second identity to also sign it + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + + // Now we need to get the second identity to sign it again to cause the error + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 3, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError( + StateError::GroupActionAlreadySignedByIdentityError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owner_requires_group_other_member_submitting_after_completion_causes_error( + ) { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_3, signer3, key3) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [ + (identity.id(), 1), + (identity_2.id(), 1), + (identity_3.id(), 1), + ] + .into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + // Now we need to get the second identity to also sign it + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + + // Now we need to get the second identity to sign it again to cause the error + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_3.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key3, + 2, + 0, + &signer3, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::GroupActionAlreadyCompletedError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(101337)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_by_owner_requires_group_proposer_not_in_group() { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_3, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity_3.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + + #[test] + fn test_token_mint_by_owner_requires_group_other_signer_not_part_of_group() { + // We are using a group, and two members need to sign for the event to happen + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_3, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_3.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_mint_serialized_transition = token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + // Now we need to get the second identity to also sign it + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + + #[test] + fn test_token_mint_other_signer_going_first_causes_error() { + // We are using a group, and the second member gets a bit hasty and signs first + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_manual_minting_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + // The second identity to also sign it + let action_id = TokenMintTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + 1337, + Some(identity.id()), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let confirm_token_mint_serialized_transition = confirm_token_mint_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[confirm_token_mint_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::GroupActionDoesNotExistError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + } +} \ No newline at end of file diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs index 4aa5901896b..69827d1ef3d 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs @@ -1,5 +1,6 @@ mod direct_selling; mod distribution; +mod mint; use super::*; use crate::execution::validation::state_transition::tests::create_token_contract_with_owner_identity; @@ -31,2865 +32,6 @@ use rand::prelude::StdRng; mod token_tests { use super::*; - mod token_mint_tests { - use super::*; - - mod token_mint_tests_normal_scenarios { - use super::*; - - #[test] - fn test_token_mint_by_owner_allowed_sending_to_self() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - None::, - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - } - - #[test] - fn test_token_mint_with_public_note() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - None::, - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - Some("this is a public note".to_string()), - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - } - - #[test] - fn test_token_mint_by_owner_can_not_mint_past_max_supply() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_max_supply(Some(1000000)); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 2000000, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owner_allowed_sending_to_other() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - None::, - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(receiver.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - receiver.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(1337)); - } - - #[test] - fn test_token_mint_sending_to_non_existing_identity_causes_error() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let receiver = Identifier::random_with_rng(&mut rng); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - None::, - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(receiver), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::RecipientIdentityDoesNotExistError( - _ - )), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - receiver.to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owner_no_destination_causes_error() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - None::, - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - None, - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::DestinationIdentityForTokenMintingNotSetError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - } - } - - mod token_mint_tests_no_recipient_minting { - use super::*; - - #[test] - fn test_token_mint_by_owned_id_allowed_sending_to_self() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owned_id_allowed_sending_to_other() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(receiver.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - receiver.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owned_id_no_destination_causes_error() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - None, - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::DestinationIdentityForTokenMintingNotSetError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - } - } - - mod token_mint_tests_contract_has_recipient { - use super::*; - - #[test] - fn test_token_mint_by_owned_id_allowed_sending_to_self() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - token_configuration - .distribution_rules_mut() - .set_new_tokens_destination_identity(Some(identity.id())); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owned_id_allowed_sending_to_other() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - token_configuration - .distribution_rules_mut() - .set_new_tokens_destination_identity(Some(identity.id())); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(receiver.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - receiver.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owned_id_no_set_destination_should_use_contracts() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration - .distribution_rules_mut() - .set_minting_allow_choosing_destination(false); - token_configuration - .distribution_rules_mut() - .set_new_tokens_destination_identity(Some(identity.id())); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - None, - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - } - } - - mod token_mint_tests_authorization_scenarios { - use super::*; - use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; - use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; - use dpp::data_contract::change_control_rules::ChangeControlRules; - use dpp::data_contract::group::v0::GroupV0; - use dpp::data_contract::group::Group; - use dpp::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; - use dpp::state_transition::batch_transition::TokenMintTransition; - - #[test] - fn test_token_mint_by_owner_sending_to_self_minting_not_allowed() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::NoOne, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - None, - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owner_sending_to_self_minting_only_allowed_by_group() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 5), (identity_2.id(), 5)].into(), - required_power: 10, - }), - )] - .into(), - ), - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owner_sending_to_self_minting_only_allowed_by_group_enough_member_power( - ) { - // We are using a group, but our member alone has enough power in the group to do the action - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 5), (identity_2.id(), 1)].into(), - required_power: 5, - }), - )] - .into(), - ), - platform_version, - ); - - let documents_batch_create_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![documents_batch_create_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - } - - #[test] - fn test_token_mint_by_owner_requires_group_other_member() { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owner_requires_group_resubmitting_causes_error() { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - // Now we need to get the second identity to also sign it, but we are going to resubmit with first - // This will create an error - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key, - 3, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError( - StateError::GroupActionAlreadySignedByIdentityError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owner_requires_group_other_member_resubmitting_causes_error() { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_3, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [ - (identity.id(), 1), - (identity_2.id(), 1), - (identity_3.id(), 1), - ] - .into(), - required_power: 3, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - - // Now we need to get the second identity to sign it again to cause the error - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 3, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError( - StateError::GroupActionAlreadySignedByIdentityError(_) - ), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owner_requires_group_other_member_submitting_after_completion_causes_error( - ) { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_3, signer3, key3) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [ - (identity.id(), 1), - (identity_2.id(), 1), - (identity_3.id(), 1), - ] - .into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - - // Now we need to get the second identity to sign it again to cause the error - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_3.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key3, - 2, - 0, - &signer3, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::GroupActionAlreadyCompletedError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(101337)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_by_owner_requires_group_proposer_not_in_group() { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_3, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity_3.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - } - - #[test] - fn test_token_mint_by_owner_requires_group_other_signer_not_part_of_group() { - // We are using a group, and two members need to sign for the event to happen - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_3, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_3.id(), 1)].into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - let token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_mint_serialized_transition = token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - // Now we need to get the second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - - #[test] - fn test_token_mint_other_signer_going_first_causes_error() { - // We are using a group, and the second member gets a bit hasty and signs first - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_manual_minting_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, - }), - )] - .into(), - ), - platform_version, - ); - - // The second identity to also sign it - let action_id = TokenMintTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let confirm_token_mint_transition = BatchTransition::new_token_mint_transition( - token_id, - identity_2.id(), - contract.id(), - 0, - 1337, - Some(identity.id()), - None, - Some( - GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( - GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - }, - ), - ), - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let confirm_token_mint_serialized_transition = confirm_token_mint_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![confirm_token_mint_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::GroupActionDoesNotExistError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, Some(100000)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - } - } - mod token_burn_tests { use super::*; diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 6bfb3741196..94437c7c594 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/mod.rs b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/mod.rs index efef30d2dfe..03dae869a07 100644 --- a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/mod.rs +++ b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/mod.rs @@ -51,6 +51,7 @@ impl Drive { contract_id: [u8; 32], group_contract_position: GroupContractPosition, action_id: Option<[u8; 32]>, + also_add_closed_tree: bool, estimated_costs_only_with_layer_info: &mut HashMap, drive_version: &DriveVersion, ) -> Result<(), Error> { @@ -65,6 +66,7 @@ impl Drive { contract_id, group_contract_position, action_id, + also_add_closed_tree, estimated_costs_only_with_layer_info, ); Ok(()) diff --git a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs index 12b15388b72..3546944698a 100644 --- a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs @@ -1,9 +1,6 @@ use crate::drive::Drive; -use crate::drive::group::paths::{ - group_action_path, group_action_signers_path, group_active_action_root_path, - group_contract_path, group_path, group_root_path, -}; +use crate::drive::group::paths::{group_action_path, group_action_signers_path, group_active_action_root_path, group_closed_action_path, group_closed_action_root_path, group_closed_action_signers_path, group_contract_path, group_path, group_root_path}; use crate::util::type_constants::DEFAULT_HASH_SIZE_U8; use dpp::data_contract::GroupContractPosition; use grovedb::batch::KeyInfoPath; @@ -57,6 +54,7 @@ impl Drive { contract_id: [u8; 32], group_contract_position: GroupContractPosition, action_id: Option<[u8; 32]>, + also_add_closed_tree: bool, estimated_costs_only_with_layer_info: &mut HashMap, ) { // DataContract_Documents 64 @@ -168,5 +166,51 @@ impl Drive { }, ); } + + if also_add_closed_tree { + estimated_costs_only_with_layer_info.insert( + KeyInfoPath::from_known_path(group_closed_action_root_path( + contract_id.as_slice(), + &group_contract_position.to_be_bytes(), + )), + EstimatedLayerInformation { + tree_type: TreeType::NormalTree, + estimated_layer_count: EstimatedLevel(10, false), + estimated_layer_sizes: AllSubtrees(DEFAULT_HASH_SIZE_U8, NoSumTrees, None), + }, + ); + + if let Some(action_id) = action_id { + estimated_costs_only_with_layer_info.insert( + KeyInfoPath::from_known_path(group_closed_action_path( + contract_id.as_slice(), + &group_contract_position.to_be_bytes(), + action_id.as_slice(), + )), + EstimatedLayerInformation { + tree_type: TreeType::NormalTree, + estimated_layer_count: EstimatedLevel(1, false), + estimated_layer_sizes: Mix { + subtrees_size: Some((1, AllSumTrees, None, 1)), + items_size: Some((1, 8, Some(36), 1)), + references_size: None, + }, + }, + ); + + estimated_costs_only_with_layer_info.insert( + KeyInfoPath::from_known_path(group_closed_action_signers_path( + contract_id.as_slice(), + &group_contract_position.to_be_bytes(), + action_id.as_slice(), + )), + EstimatedLayerInformation { + tree_type: TreeType::SumTree, + estimated_layer_count: EstimatedLevel(1, false), + estimated_layer_sizes: AllItems(8, 1, None), + }, + ); + } + } } } diff --git a/packages/rs-drive/src/drive/group/insert/add_group_action/mod.rs b/packages/rs-drive/src/drive/group/insert/add_group_action/mod.rs index b8370cfa30c..37642fc5bf3 100644 --- a/packages/rs-drive/src/drive/group/insert/add_group_action/mod.rs +++ b/packages/rs-drive/src/drive/group/insert/add_group_action/mod.rs @@ -24,6 +24,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -37,6 +38,7 @@ impl Drive { contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, @@ -60,6 +62,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -74,6 +77,7 @@ impl Drive { contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, @@ -97,6 +101,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -112,6 +117,7 @@ impl Drive { contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, diff --git a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs index 8293dbec897..b55b45e7382 100644 --- a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs @@ -1,11 +1,8 @@ -use crate::drive::group::paths::{ - group_action_path, group_action_signers_path_vec, group_active_action_root_path, - ACTION_INFO_KEY, ACTION_SIGNERS_KEY, -}; +use crate::drive::group::paths::{group_action_path, group_action_signers_path_vec, group_active_action_root_path, group_closed_action_path, group_closed_action_root_path, group_closed_action_signers_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY}; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; -use crate::util::grove_operations::{BatchInsertApplyType, BatchInsertTreeApplyType, QueryTarget}; +use crate::util::grove_operations::{BatchInsertApplyType, BatchInsertTreeApplyType, BatchMoveApplyType, QueryTarget}; use crate::util::object_size_info::PathKeyInfo::PathFixedSizeKeyRef; use crate::util::object_size_info::{DriveKeyInfo, PathKeyElementInfo}; use dpp::block::block_info::BlockInfo; @@ -21,6 +18,8 @@ use grovedb::element::SumValue; use grovedb::{Element, EstimatedLayerInformation, TransactionArg, TreeType}; use grovedb_epoch_based_storage_flags::StorageFlags; use std::collections::HashMap; +use grovedb::MaybeTree::NotTree; +use dpp::group::group_action_status::GroupActionStatus; impl Drive { #[allow(clippy::too_many_arguments)] @@ -29,6 +28,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -42,6 +42,7 @@ impl Drive { contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, @@ -69,6 +70,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -88,6 +90,7 @@ impl Drive { contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, @@ -113,6 +116,7 @@ impl Drive { contract_id: Identifier, group_contract_position: GroupContractPosition, initialize_with_insert_action_info: Option, + closes_group_action: bool, action_id: Identifier, signer_identity_id: Identifier, signer_power: GroupMemberPower, @@ -130,112 +134,252 @@ impl Drive { contract_id.to_buffer(), group_contract_position, Some(action_id.to_buffer()), + closes_group_action, estimated_costs_only_with_layer_info, &platform_version.drive, )?; } let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); - let group_action_root_path = group_active_action_root_path( - contract_id.as_slice(), - group_contract_position_bytes.as_slice(), - ); - let apply_type = if estimated_costs_only_with_layer_info.is_none() { - BatchInsertTreeApplyType::StatefulBatchInsertTree - } else { - BatchInsertTreeApplyType::StatelessBatchInsertTree { - in_tree_type: TreeType::NormalTree, - tree_type: TreeType::NormalTree, - flags_len: 0, - } - }; + + if !closes_group_action { + let group_action_root_path = group_active_action_root_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + ); + let apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchInsertTreeApplyType::StatefulBatchInsertTree + } else { + BatchInsertTreeApplyType::StatelessBatchInsertTree { + in_tree_type: TreeType::NormalTree, + tree_type: TreeType::NormalTree, + flags_len: 0, + } + }; - let storage_flags = Some(StorageFlags::new_single_epoch( - block_info.epoch.index, - Some(signer_identity_id.to_buffer()), - )); + let storage_flags = Some(StorageFlags::new_single_epoch( + block_info.epoch.index, + Some(signer_identity_id.to_buffer()), + )); - let element_flags = StorageFlags::map_to_some_element_flags(storage_flags.as_ref()); + let element_flags = StorageFlags::map_to_some_element_flags(storage_flags.as_ref()); - let mut inserted_root_action = false; + let mut inserted_root_action = false; - if let Some(initialize_with_insert_action_info) = initialize_with_insert_action_info { - // We insert the contract root into the group tree - inserted_root_action = self.batch_insert_empty_tree_if_not_exists( - PathFixedSizeKeyRef((group_action_root_path, action_id.as_slice())), - TreeType::NormalTree, - None, - apply_type, - transaction, - &mut None, - &mut batch_operations, - &platform_version.drive, - )?; + if let Some(initialize_with_insert_action_info) = initialize_with_insert_action_info { + // We insert the contract root into the group tree + inserted_root_action = self.batch_insert_empty_tree_if_not_exists( + PathFixedSizeKeyRef((group_action_root_path, action_id.as_slice())), + TreeType::NormalTree, + None, + apply_type, + transaction, + &mut None, + &mut batch_operations, + &platform_version.drive, + )?; + + if inserted_root_action { + let group_action_path = group_action_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + action_id.as_slice(), + ); + + self.batch_insert_empty_sum_tree( + group_action_path, + DriveKeyInfo::KeyRef(ACTION_SIGNERS_KEY), + None, + &mut batch_operations, + &platform_version.drive, + )?; + + let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + + self.batch_insert( + PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( + group_action_path, + ACTION_INFO_KEY, + Element::Item(serialized, element_flags.clone()), + )), + &mut batch_operations, + &platform_version.drive, + )?; + } + } + + let signers_path = group_action_signers_path_vec( + contract_id.as_slice(), + group_contract_position, + action_id.as_slice(), + ); + + let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchInsertApplyType::StatefulBatchInsert + } else { + BatchInsertApplyType::StatelessBatchInsert { + in_tree_type: TreeType::SumTree, + target: QueryTarget::QueryTargetValue(8), + } + }; if inserted_root_action { - let group_action_path = group_action_path( - contract_id.as_slice(), - group_contract_position_bytes.as_slice(), - action_id.as_slice(), - ); - - self.batch_insert_empty_sum_tree( - group_action_path, - DriveKeyInfo::KeyRef(ACTION_SIGNERS_KEY), + self.batch_insert( + PathKeyElementInfo::PathKeyElement::<0>(( + signers_path, + signer_identity_id.to_vec(), + Element::SumItem(signer_power as SumValue, element_flags), + )), + &mut batch_operations, + &platform_version.drive, + )?; + } else { + // we should verify it doesn't yet exist + self.batch_insert_sum_item_if_not_exists( + PathKeyElementInfo::PathKeyElement::<0>(( + signers_path, + signer_identity_id.to_vec(), + Element::SumItem(signer_power as SumValue, element_flags), + )), + true, + signer_apply_type, + transaction, + &mut batch_operations, + &platform_version.drive, + )?; + } + } else { + // We are closing the group action + let group_action_root_path = group_closed_action_root_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + ); + let apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchInsertTreeApplyType::StatefulBatchInsertTree + } else { + BatchInsertTreeApplyType::StatelessBatchInsertTree { + in_tree_type: TreeType::NormalTree, + tree_type: TreeType::NormalTree, + flags_len: 0, + } + }; + + let mut inserted_root_action = false; + + if let Some(initialize_with_insert_action_info) = initialize_with_insert_action_info { + // We insert the contract root into the group tree + inserted_root_action = self.batch_insert_empty_tree_if_not_exists( + PathFixedSizeKeyRef((group_action_root_path, action_id.as_slice())), + TreeType::NormalTree, None, + apply_type, + transaction, + &mut None, &mut batch_operations, &platform_version.drive, )?; - let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + if inserted_root_action { + let group_action_path = group_closed_action_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + action_id.as_slice(), + ); - self.batch_insert( - PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( + self.batch_insert_empty_sum_tree( group_action_path, - ACTION_INFO_KEY, - Element::Item(serialized, element_flags.clone()), + DriveKeyInfo::KeyRef(ACTION_SIGNERS_KEY), + None, + &mut batch_operations, + &platform_version.drive, + )?; + + let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + + self.batch_insert( + PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( + group_action_path, + ACTION_INFO_KEY, + Element::Item(serialized, None), + )), + &mut batch_operations, + &platform_version.drive, + )?; + } + } + + let closed_signers_path = group_closed_action_signers_path_vec( + contract_id.as_slice(), + group_contract_position, + action_id.as_slice(), + ); + + let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchInsertApplyType::StatefulBatchInsert + } else { + BatchInsertApplyType::StatelessBatchInsert { + in_tree_type: TreeType::SumTree, + target: QueryTarget::QueryTargetValue(8), + } + }; + + if inserted_root_action { + self.batch_insert( + PathKeyElementInfo::PathKeyElement::<0>(( + closed_signers_path.clone(), + signer_identity_id.to_vec(), + Element::SumItem(signer_power as SumValue, None), )), &mut batch_operations, &platform_version.drive, )?; + } else { + // we should verify it doesn't yet exist + self.batch_insert_sum_item_if_not_exists( + PathKeyElementInfo::PathKeyElement::<0>(( + closed_signers_path.clone(), + signer_identity_id.to_vec(), + Element::SumItem(signer_power as SumValue, None), + )), + true, + signer_apply_type, + transaction, + &mut batch_operations, + &platform_version.drive, + )?; } - } + + // now we need to also move all signers from the active tree to the closed tree - let signers_path = group_action_signers_path_vec( - contract_id.as_slice(), - group_contract_position, - action_id.as_slice(), - ); + let mut active_path_query = Drive::group_action_signers_query( + contract_id.to_buffer(), + group_contract_position, + GroupActionStatus::ActionActive, + action_id.to_buffer(), + ); - let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { - BatchInsertApplyType::StatefulBatchInsert - } else { - BatchInsertApplyType::StatelessBatchInsert { - in_tree_type: TreeType::SumTree, - target: QueryTarget::QueryTargetValue(8), - } - }; + active_path_query.query.limit = Some(platform_version.system_limits.max_contract_group_size); - if inserted_root_action { - self.batch_insert( - PathKeyElementInfo::PathKeyElement::<0>(( - signers_path, - signer_identity_id.to_vec(), - Element::SumItem(signer_power as SumValue, element_flags), - )), - &mut batch_operations, - &platform_version.drive, - )?; - } else { - // we should verify it doesn't yet exist - self.batch_insert_sum_item_if_not_exists( - PathKeyElementInfo::PathKeyElement::<0>(( - signers_path, - signer_identity_id.to_vec(), - Element::SumItem(signer_power as SumValue, element_flags), - )), - true, + let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum: Some(NotTree), + } + } else { + BatchMoveApplyType::StatelessBatchMove { + in_tree_type: TreeType::SumTree, + tree_type: None, + estimated_key_size: 32, + estimated_value_size: 8, + flags_len: 0, + } + }; + + self.batch_move_items_in_path_query( + &active_path_query, + closed_signers_path.clone(), + false, // maybe there were no active signers signer_apply_type, + Some(None), // we should have no flags on the closed items transaction, &mut batch_operations, &platform_version.drive, diff --git a/packages/rs-drive/src/drive/group/queries.rs b/packages/rs-drive/src/drive/group/queries.rs index e78e6cca882..2a84cac6dd6 100644 --- a/packages/rs-drive/src/drive/group/queries.rs +++ b/packages/rs-drive/src/drive/group/queries.rs @@ -1,7 +1,4 @@ -use crate::drive::group::paths::{ - group_contract_path_vec, group_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY, - GROUP_ACTIVE_ACTIONS_KEY, GROUP_CLOSED_ACTIONS_KEY, GROUP_INFO_KEY, -}; +use crate::drive::group::paths::{group_action_signers_path_vec, group_closed_action_signers_path_vec, group_contract_path_vec, group_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY, GROUP_ACTIVE_ACTIONS_KEY, GROUP_CLOSED_ACTIONS_KEY, GROUP_INFO_KEY}; use crate::drive::Drive; use crate::query::{Query, QueryItem}; use dpp::data_contract::GroupContractPosition; @@ -90,6 +87,35 @@ impl Drive { } } + /// Gets the action signers query + pub fn group_action_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + action_status: GroupActionStatus, + action_id: [u8; 32], + ) -> PathQuery { + let mut group_actions_path = group_path_vec(&contract_id, group_contract_position); + match action_status { + GroupActionStatus::ActionActive => { + group_actions_path.push(GROUP_ACTIVE_ACTIONS_KEY.to_vec()) + } + GroupActionStatus::ActionClosed => { + group_actions_path.push(GROUP_CLOSED_ACTIONS_KEY.to_vec()) + } + } + group_actions_path.push(action_id.to_vec()); + let query = Query::new_single_key(ACTION_SIGNERS_KEY.to_vec()); + + PathQuery { + path: group_actions_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } + /// Gets the action signers query pub fn group_action_signers_query( contract_id: [u8; 32], @@ -119,4 +145,97 @@ impl Drive { }, } } + + /// Query for a single active signer + pub fn group_active_action_single_signer_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + action_id: [u8; 32], + identity_id: [u8; 32], + ) -> PathQuery { + let group_actions_path = group_action_signers_path_vec(&contract_id, group_contract_position, &action_id); + let query = Query::new_single_key(identity_id.to_vec()); + + PathQuery { + path: group_actions_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } + + /// Query for a single active signer + pub fn group_closed_action_single_signer_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + action_id: [u8; 32], + identity_id: [u8; 32], + ) -> PathQuery { + let group_actions_path = group_closed_action_signers_path_vec(&contract_id, group_contract_position, &action_id); + let query = Query::new_single_key(identity_id.to_vec()); + + PathQuery { + path: group_actions_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } + + /// Query for a single active signer + pub fn group_active_or_closed_action_single_signer_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + action_id: [u8; 32], + action_status: GroupActionStatus, + identity_id: [u8; 32], + ) -> PathQuery { + let mut group_actions_path = group_path_vec(&contract_id, group_contract_position); + match action_status { + GroupActionStatus::ActionActive => { + group_actions_path.push(GROUP_ACTIVE_ACTIONS_KEY.to_vec()) + } + GroupActionStatus::ActionClosed => { + group_actions_path.push(GROUP_CLOSED_ACTIONS_KEY.to_vec()) + } + } + group_actions_path.push(action_id.to_vec()); + group_actions_path.push(ACTION_SIGNERS_KEY.to_vec()); + let query = Query::new_single_key(identity_id.to_vec()); + + PathQuery { + path: group_actions_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } + + /// Gets the signer in both the active and closed action places + pub fn group_active_and_closed_action_single_signer_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + action_id: [u8; 32], + identity_id: [u8; 32], + ) -> PathQuery { + let group_path = group_path_vec(&contract_id, group_contract_position); + let mut query = Query::new_with_direction(true); + query.insert_keys(vec![GROUP_ACTIVE_ACTIONS_KEY.to_vec(), GROUP_CLOSED_ACTIONS_KEY.to_vec()]); + query.set_subquery_path(vec![action_id.to_vec(), ACTION_SIGNERS_KEY.to_vec(), identity_id.to_vec()]); + + PathQuery { + path: group_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } } diff --git a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs index 977db6d6533..955d8fc0eb1 100644 --- a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs +++ b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs @@ -1,3 +1,4 @@ +use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; use crate::drive::Drive; use crate::error::proof::ProofError; use crate::error::Error; @@ -6,9 +7,6 @@ use crate::query::{ IdentityBasedVoteDriveQuery, SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus, }; use dpp::data_contract::accessors::v0::DataContractV0Getters; -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_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contracts::SystemDataContract; use dpp::identifier::Identifier; @@ -22,9 +20,6 @@ use dpp::state_transition::batch_transition::batched_transition::token_transitio use dpp::state_transition::batch_transition::batched_transition::BatchedTransitionRef; use dpp::state_transition::batch_transition::document_base_transition::v0::v0_methods::DocumentBaseTransitionV0Methods; use dpp::state_transition::batch_transition::document_create_transition::v0::v0_methods::DocumentCreateTransitionV0Methods; -use dpp::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; -use dpp::state_transition::batch_transition::token_mint_transition::v0::v0_methods::TokenMintTransitionV0Methods; -use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_methods::TokenTransferTransitionV0Methods; use dpp::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0; use dpp::state_transition::identity_credit_transfer_transition::accessors::IdentityCreditTransferTransitionAccessorsV0; use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; @@ -113,7 +108,7 @@ impl Drive { )? else { return Err(Error::Proof(ProofError::UnknownContract(format!( - "unknown contract with id {}", + "unknown contract with id {} in document proving", data_contract_id )))); }; @@ -158,9 +153,7 @@ impl Drive { path_query } BatchedTransitionRef::Token(token_transition) => { - //todo group actions let data_contract_id = token_transition.data_contract_id(); - let token_id = token_transition.token_id(); let Some(contract_fetch_info) = self.get_contract_with_fetch_info( data_contract_id.to_buffer(), @@ -171,132 +164,14 @@ impl Drive { else { return Ok(ProofCreationResult::new_with_error( ProofError::UnknownContract(format!( - "unknown contract with id {}", + "unknown contract with id {} in token proving", data_contract_id )), )); }; let contract = &contract_fetch_info.contract; - - let token_config = contract.expected_token_configuration( - token_transition.base().token_contract_position(), - )?; - - let keeps_historical_document = token_config.keeps_history(); - - match token_transition { - TokenTransition::Burn(_) => { - if keeps_historical_document.keeps_burning_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - Drive::token_balance_for_identity_id_query( - token_id.to_buffer(), - owner_id.to_buffer(), - ) - } - } - TokenTransition::Mint(token_mint_transition) => { - if keeps_historical_document.keeps_minting_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - let recipient_id = - token_mint_transition.recipient_id(token_config)?; - - Drive::token_balance_for_identity_id_query( - token_id.into_buffer(), - recipient_id.into_buffer(), - ) - } - } - TokenTransition::Transfer(token_transfer_transition) => { - if keeps_historical_document.keeps_transfer_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - let recipient_id = token_transfer_transition.recipient_id(); - let identity_ids = - [owner_id.to_buffer(), recipient_id.to_buffer()]; - - Drive::token_balances_for_identity_ids_query( - token_id.into_buffer(), - &identity_ids, - ) - } - } - TokenTransition::Freeze(_) => { - if keeps_historical_document.keeps_freezing_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - Drive::token_info_for_identity_id_query( - token_id.to_buffer(), - owner_id.to_buffer(), - ) - } - } - TokenTransition::Unfreeze(_) => { - if keeps_historical_document.keeps_freezing_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - Drive::token_info_for_identity_id_query( - token_id.to_buffer(), - owner_id.to_buffer(), - ) - } - } - TokenTransition::DirectPurchase(_) => { - if keeps_historical_document.keeps_direct_purchase_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - Drive::token_balance_for_identity_id_query( - token_id.to_buffer(), - owner_id.to_buffer(), - ) - } - } - TokenTransition::SetPriceForDirectPurchase(_) => { - if keeps_historical_document.keeps_direct_pricing_history() { - create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )? - } else { - Drive::token_direct_purchase_price_query(token_id.to_buffer()) - } - } - TokenTransition::DestroyFrozenFunds(_) - | TokenTransition::EmergencyAction(_) - | TokenTransition::ConfigUpdate(_) - | TokenTransition::Claim(_) => create_token_historical_document_query( - token_transition, - owner_id, - platform_version, - )?, - } + token_transition.try_transition_into_path_query_with_contract(contract, owner_id, platform_version)? } } } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs index b500818b219..b266e5ca9c3 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs @@ -69,6 +69,7 @@ impl DriveHighLevelBatchOperationConverter for TokenBurnTransitionAction { action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs index 90eca44d98f..6b305eacbab 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs @@ -73,6 +73,7 @@ impl DriveHighLevelBatchOperationConverter for TokenConfigUpdateTransitionAction action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs index 7f40fdd6960..46060cb63cb 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs @@ -68,6 +68,7 @@ impl DriveHighLevelBatchOperationConverter for TokenDestroyFrozenFundsTransition action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs index 7e9470b9972..120d5245421 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs @@ -68,6 +68,7 @@ impl DriveHighLevelBatchOperationConverter for TokenEmergencyActionTransitionAct action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs index 0d453ed4a20..602a2c88265 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs @@ -72,6 +72,7 @@ impl DriveHighLevelBatchOperationConverter for TokenFreezeTransitionAction { action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs index abc006dae2d..08973a2cb17 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs @@ -73,6 +73,7 @@ impl DriveHighLevelBatchOperationConverter for TokenMintTransitionAction { action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs index 51c92301eb2..60db43c86ea 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs @@ -72,6 +72,7 @@ impl DriveHighLevelBatchOperationConverter for TokenSetPriceForDirectPurchaseTra action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs index 0758e6955d4..3a7da4b0490 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs @@ -72,6 +72,7 @@ impl DriveHighLevelBatchOperationConverter for TokenUnfreezeTransitionAction { action_id: *action_id, signer_identity_id: owner_id, signer_power: *signer_power, + closes_group_action: self.base().perform_action(), })); } diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/group.rs b/packages/rs-drive/src/util/batch/drive_op_batch/group.rs index ce1a193c451..51ca801b4af 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/group.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/group.rs @@ -29,6 +29,8 @@ pub enum GroupOperationType { signer_identity_id: Identifier, /// The signer's power in the group signer_power: GroupMemberPower, + /// Should we close the group action and mark it as complete? + closes_group_action: bool }, } @@ -51,10 +53,12 @@ impl DriveLowLevelOperationConverter for GroupOperationType { action_id, signer_identity_id, signer_power, + closes_group_action, } => drive.add_group_action_operations( contract_id, group_contract_position, initialize_with_insert_action_info, + closes_group_action, action_id, signer_identity_id, signer_power, diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs b/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs index 8146f534d71..d1144a7d68d 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs @@ -166,6 +166,7 @@ impl DriveLowLevelOperationConverter for WithdrawalOperationType { BatchMoveApplyType::StatefulBatchMove { is_known_to_be_subtree_with_sum: Some(MaybeTree::NotTree), }, + None, transaction, &mut drive_operations, &platform_version.drive, @@ -207,6 +208,7 @@ impl DriveLowLevelOperationConverter for WithdrawalOperationType { BatchMoveApplyType::StatefulBatchMove { is_known_to_be_subtree_with_sum: Some(MaybeTree::NotTree), }, + None, transaction, &mut drive_operations, &platform_version.drive, diff --git a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/mod.rs index 452a7ef07e7..7c31d0b7a66 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/mod.rs @@ -9,6 +9,7 @@ use crate::util::grove_operations::BatchMoveApplyType; use dpp::version::drive_versions::DriveVersion; use grovedb::{PathQuery, TransactionArg}; +use grovedb_epoch_based_storage_flags::StorageFlags; impl Drive { /// Pushes multiple "delete element" and "insert element operations for items in a given path based on a `PathQuery` to `drive_operations`. @@ -17,6 +18,7 @@ impl Drive { /// * `path_query`: The path query specifying the items to delete within the path. /// * `error_if_intermediate_path_tree_not_present`: Tells the function to either error or do nothing if an intermediate tree is not present. /// * `apply_type`: The apply type for the move operations. + /// * `alter_flags_to_new_flags`: Should we alter flags of items to new flags /// * `transaction`: The transaction argument. /// * `drive_operations`: The vector containing low-level drive operations. /// * `drive_version`: The drive version to select the correct function version to run. @@ -31,6 +33,7 @@ impl Drive { new_path: Vec>, error_if_intermediate_path_tree_not_present: bool, apply_type: BatchMoveApplyType, + alter_flags_to_new_flags: Option>, transaction: TransactionArg, drive_operations: &mut Vec, drive_version: &DriveVersion, @@ -45,6 +48,7 @@ impl Drive { new_path, error_if_intermediate_path_tree_not_present, apply_type, + alter_flags_to_new_flags, transaction, drive_operations, drive_version, diff --git a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs index ff5f031ea54..502943431b1 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs @@ -9,6 +9,7 @@ use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp}; use grovedb::operations::delete::DeleteOptions; use grovedb::query_result_type::QueryResultType; use grovedb::{GroveDb, PathQuery, TransactionArg}; +use grovedb_epoch_based_storage_flags::StorageFlags; use grovedb_storage::rocksdb_storage::RocksDbStorage; use platform_version::version::drive_versions::DriveVersion; @@ -21,6 +22,7 @@ impl Drive { /// * `path_query`: The path query specifying the items to delete within the path. /// * `error_if_intermediate_path_tree_not_present`: Tells the function to either error or do nothing if an intermediate tree is not present. /// * `apply_type`: The apply type for the move operations. + /// * `alter_flags_to_new_flags`: Should we alter the flags to new storage flags. /// * `transaction`: The transaction argument. /// * `drive_operations`: The vector containing low-level drive operations. /// * `drive_version`: The drive version to select the correct function version to run. @@ -35,6 +37,7 @@ impl Drive { new_path: Vec>, error_if_intermediate_path_tree_not_present: bool, apply_type: BatchMoveApplyType, + alter_flags_to_new_flags: Option>, transaction: TransactionArg, drive_operations: &mut Vec, drive_version: &DriveVersion, @@ -79,7 +82,7 @@ impl Drive { }; // Iterate over each element and add a delete operation for it - for (path, key, element) in query_result { + for (path, key, mut element) in query_result { let current_batch_operations = LowLevelDriveOperation::grovedb_operations_batch(drive_operations); let options = DeleteOptions { @@ -121,6 +124,9 @@ impl Drive { if let Some(delete_operation) = push_drive_operation_result(delete_operation, drive_operations)? { + if let Some(altered_flags) = alter_flags_to_new_flags.as_ref() { + element.set_flags(StorageFlags::map_to_some_element_flags(altered_flags.as_ref())) + } // Add the delete operation to the batch of drive operations drive_operations.push(GroveOperation(delete_operation)); // Adds the insert operation to the batch of drive operations @@ -230,6 +236,7 @@ mod tests { new_path.clone(), true, apply_type, + None, Some(&transaction), &mut drive_operations, &platform_version.drive, @@ -341,6 +348,7 @@ mod tests { new_path.clone(), true, apply_type, + None, Some(&transaction), &mut drive_operations, &platform_version.drive, @@ -445,6 +453,7 @@ mod tests { new_path.clone(), true, apply_type, + None, Some(&transaction), &mut drive_operations, &platform_version.drive, diff --git a/packages/rs-drive/src/util/grove_operations/mod.rs b/packages/rs-drive/src/util/grove_operations/mod.rs index 730a90230b0..caf8d351a60 100644 --- a/packages/rs-drive/src/util/grove_operations/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/mod.rs @@ -212,10 +212,10 @@ pub enum BatchDeleteApplyType { pub enum BatchMoveApplyType { /// Stateless batch move StatelessBatchMove { - /// Are we moving from inside a sum tree + /// What type of tree are we in for the move in_tree_type: TreeType, - /// Are we moving a sum tree - tree_type: TreeType, + /// Are we moving a trees? + tree_type: Option, /// What is the estimated key size estimated_key_size: u32, /// What is the estimated value size diff --git a/packages/rs-drive/src/verify/group/mod.rs b/packages/rs-drive/src/verify/group/mod.rs index d77cec3ea0f..17438f8f624 100644 --- a/packages/rs-drive/src/verify/group/mod.rs +++ b/packages/rs-drive/src/verify/group/mod.rs @@ -2,3 +2,4 @@ mod verify_action_signers; mod verify_active_action_infos; mod verify_group_info; mod verify_group_infos_in_contract; +mod verify_action_signers_total_power; diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs new file mode 100644 index 00000000000..09333eb59f5 --- /dev/null +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs @@ -0,0 +1,82 @@ +mod v0; + +use crate::drive::Drive; +use dpp::data_contract::group::{GroupMemberPower, GroupSumPower}; +use dpp::data_contract::GroupContractPosition; +use dpp::group::group_action_status::GroupActionStatus; +use dpp::identifier::Identifier; + +use crate::error::drive::DriveError; + +use crate::error::Error; + +use crate::verify::RootHash; + +use dpp::version::PlatformVersion; + +impl Drive { + /// Verifies the total signing power for a given group action using a Merkle proof. + /// + /// This function checks the integrity and validity of signer entries in the group action's signer sum tree + /// by processing a GroveDB proof. It computes the total `GroupSumPower` of all signers recorded for the action. + /// + /// The method dispatches to a versioned implementation (currently only version 0 is supported). + /// + /// # Parameters + /// - `proof`: A byte slice containing a GroveDB cryptographic proof (e.g., a query proof). + /// - `contract_id`: The identifier of the data contract defining the group. + /// - `group_contract_position`: The position/index of the group in the contract. + /// - `action_status`: Whether the group action is currently `Active` or `Closed`. + /// - `action_id`: The unique identifier of the group action being verified. + /// - `verify_subset_of_proof`: If `true`, verifies only the relevant part of the proof for optimization. If `false`, verifies the full structure. + /// - `platform_version`: A reference to the current `PlatformVersion`, used to determine the correct versioned implementation. + /// + /// # Returns + /// A `Result` with: + /// - `Ok((root_hash, total_power))` — where: + /// - `root_hash`: The Merkle root hash verified during the proof process. + /// - `total_power`: The combined `GroupSumPower` of all verified signers. + /// - `Err(Error)` — if the version is unknown or the proof is invalid. + /// + /// # Errors + /// - [`Error::Drive(DriveError::UnknownVersionMismatch)`] if the method version is unsupported. + /// - Other variants of [`Error`] may occur if the proof cannot be parsed or verified. + /// + /// # Versioning + /// - Dispatches to `verify_action_signers_total_power_v0` when `platform_version.drive.methods.verify.group.verify_action_signers_total_power == 0`. + /// + pub fn verify_action_signer_and_total_power( + proof: &[u8], + contract_id: Identifier, + group_contract_position: GroupContractPosition, + action_status: GroupActionStatus, + action_id: Identifier, + action_signer_id: Identifier, + verify_subset_of_proof: bool, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, GroupSumPower), Error> { + match platform_version + .drive + .methods + .verify + .group + .verify_action_signers_total_power + { + 0 => Self::verify_action_signers_total_power_v0( + proof, + contract_id, + group_contract_position, + action_status, + action_id, + action_signer_id, + verify_subset_of_proof, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "verify_action_signers_total_power".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs new file mode 100644 index 00000000000..58089623dfe --- /dev/null +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs @@ -0,0 +1,67 @@ +use crate::drive::Drive; +use grovedb::Element::{SumItem, SumTree}; + +use crate::error::proof::ProofError; +use crate::error::Error; + +use crate::verify::RootHash; + +use dpp::data_contract::group::{GroupMemberPower, GroupSumPower}; +use dpp::data_contract::GroupContractPosition; +use dpp::group::group_action_status::GroupActionStatus; +use dpp::identifier::Identifier; +use grovedb::{GroveDb, TreeFeatureType}; +use platform_version::version::PlatformVersion; + +impl Drive { + pub(super) fn verify_action_signers_total_power_v0( + proof: &[u8], + contract_id: Identifier, + group_contract_position: GroupContractPosition, + action_status: GroupActionStatus, + action_id: Identifier, + action_signer_id: Identifier, + verify_subset_of_proof: bool, + platform_version: &PlatformVersion, + ) -> Result<(RootHash, GroupSumPower), Error> { + let path_query = Drive::group_active_or_closed_action_single_signer_query( + contract_id.to_buffer(), + group_contract_position, + action_id.to_buffer(), + action_status, + action_signer_id.to_buffer() + ); + + let (root_hash, tree_feature, mut proved_key_values) = if verify_subset_of_proof { + GroveDb::verify_subset_query_get_parent_tree_info(proof, &path_query, &platform_version.drive.grove_version)? + } else { + GroveDb::verify_query_get_parent_tree_info(proof, &path_query, &platform_version.drive.grove_version)? + }; + + if proved_key_values.len() != 1 { + return Err(Error::Proof(ProofError::CorruptedProof(format!( + "we should always get back one group power, we got {}", + proved_key_values.len() + )))); + } + + let element = proved_key_values.remove(0).2; + match element { + Some(SumItem(..)) => { + if let TreeFeatureType::SummedMerkNode(aggregate_power) = tree_feature { + Ok((root_hash, aggregate_power as GroupSumPower)) + } else { + Err(Error::Proof(ProofError::IncorrectProof( + "we expected a summed tree".to_string(), + ))) + } + } + None => Err(Error::Proof(ProofError::IncorrectProof( + "we expect to get back the signing power".to_string(), + ))), + _ => Err(Error::Proof(ProofError::IncorrectProof( + "element should be a sum tree representing total signed power".to_string(), + ))), + } + } +} diff --git a/packages/rs-drive/src/verify/state_transition/mod.rs b/packages/rs-drive/src/verify/state_transition/mod.rs index cc466d86d2f..0d27f86f729 100644 --- a/packages/rs-drive/src/verify/state_transition/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/mod.rs @@ -1 +1,5 @@ +/// Provides functions to verify that a state transition was executed using a cryptographic proof. pub mod verify_state_transition_was_executed_with_proof; + +/// Defines path queries needed to verify state transitions based on their type and expected result. +pub mod state_transition_execution_path_queries; \ No newline at end of file diff --git a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs new file mode 100644 index 00000000000..502b48467cc --- /dev/null +++ b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs @@ -0,0 +1,48 @@ +use grovedb::PathQuery; +use dpp::identifier::Identifier; +use dpp::prelude::DataContract; +use platform_version::version::PlatformVersion; + +/// Token transition module +pub mod token_transition; +/// A trait for converting a state transition into a [`PathQuery`] for data retrieval. +/// +/// This is used to express the expected changes or queries required to validate or process +/// a state transition, particularly in the context of tokens or document transitions where +/// path-based access is necessary (e.g., GroveDB queries). +/// +/// # Associated Types +/// * `Error` – the error type returned when the conversion fails. +/// +/// # Required Methods +/// +/// ## `try_transition_into_path_query_with_contract` +/// Attempts to convert the transition into a path query based on the provided data contract, +/// owner identity, and platform version. This is often used for querying relevant token or +/// document paths associated with the transition. +/// +/// ### Parameters +/// * `data_contract`: A reference to the [`DataContract`] defining the schema. +/// * `owner_id`: The identifier of the identity owning the transition. +/// * `platform_version`: The current [`PlatformVersion`] used for versioned logic. +/// +/// ### Returns +/// * `Ok(PathQuery)` – a [`PathQuery`] representing the database paths needed for verification or processing. +/// * `Err(Self::Error)` – if the transition could not be converted to a path query. +/// +/// # Usage +/// This trait is typically implemented for specific transition types (e.g., token minting) +/// that require prefetching or validation of state from GroveDB. +/// +pub trait TryTransitionIntoPathQuery { + /// The error type returned on conversion failure. + type Error; + + /// Attempts to convert a transition into a corresponding [`PathQuery`] based on the contract and owner. + fn try_transition_into_path_query_with_contract( + &self, + data_contract: &DataContract, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result; +} \ No newline at end of file diff --git a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs new file mode 100644 index 00000000000..6f576be5f3d --- /dev/null +++ b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs @@ -0,0 +1,195 @@ +use grovedb::PathQuery; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +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_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Getters; +use dpp::data_contracts::SystemDataContract; +use dpp::group::GroupStateTransitionInfo; +use dpp::identifier::Identifier; +use dpp::prelude::DataContract; +use dpp::state_transition::batch_transition::batched_transition::token_transition::{TokenTransition, TokenTransitionV0Methods}; +use dpp::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; +use dpp::state_transition::batch_transition::token_mint_transition::v0::v0_methods::TokenMintTransitionV0Methods; +use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_methods::TokenTransferTransitionV0Methods; +use dpp::system_data_contracts::load_system_data_contract; +use platform_version::version::PlatformVersion; +use crate::drive::Drive; +use crate::error::Error; +use crate::query::{SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus}; +use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; + +fn create_token_historical_document_query( + token_transition: &TokenTransition, + owner_id: Identifier, + platform_version: &PlatformVersion, +) -> Result { + let token_history_contract = + load_system_data_contract(SystemDataContract::TokenHistory, platform_version)?; + + let query = SingleDocumentDriveQuery { + contract_id: token_history_contract.id().into_buffer(), + document_type_name: token_transition.historical_document_type_name().to_string(), + document_type_keeps_history: false, + document_id: token_transition + .historical_document_id(owner_id) + .to_buffer(), + block_time_ms: None, //None because we want latest + contested_status: SingleDocumentDriveQueryContestedStatus::NotContested, + }; + + query.construct_path_query(platform_version) +} + +fn create_token_group_action_query( + contract_id: [u8;32], + identity_id: [u8;32], + group_state_transition_info: GroupStateTransitionInfo +) -> PathQuery { + let GroupStateTransitionInfo { + group_contract_position, action_id, .. + } =group_state_transition_info; + + Drive::group_active_and_closed_action_single_signer_query(contract_id, group_contract_position, action_id.to_buffer(), identity_id) +} + +impl TryTransitionIntoPathQuery for TokenTransition { + type Error = Error; + + fn try_transition_into_path_query_with_contract(&self, contract: &DataContract, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + let token_id = self.token_id(); + + let token_config = contract.expected_token_configuration( + self.base().token_contract_position(), + )?; + + let keeps_historical_document = token_config.keeps_history(); + + let action_success_query = match self { + TokenTransition::Burn(_) => { + if keeps_historical_document.keeps_burning_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + Drive::token_balance_for_identity_id_query( + token_id.to_buffer(), + owner_id.to_buffer(), + ) + } + } + TokenTransition::Mint(token_mint_transition) => { + if keeps_historical_document.keeps_minting_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + let recipient_id = + token_mint_transition.recipient_id(token_config)?; + + Drive::token_balance_for_identity_id_query( + token_id.into_buffer(), + recipient_id.into_buffer(), + ) + } + } + TokenTransition::Transfer(token_transfer_transition) => { + if keeps_historical_document.keeps_transfer_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + let recipient_id = token_transfer_transition.recipient_id(); + let identity_ids = + [owner_id.to_buffer(), recipient_id.to_buffer()]; + + Drive::token_balances_for_identity_ids_query( + token_id.into_buffer(), + &identity_ids, + ) + } + } + TokenTransition::Freeze(_) => { + if keeps_historical_document.keeps_freezing_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + Drive::token_info_for_identity_id_query( + token_id.to_buffer(), + owner_id.to_buffer(), + ) + } + } + TokenTransition::Unfreeze(_) => { + if keeps_historical_document.keeps_freezing_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + Drive::token_info_for_identity_id_query( + token_id.to_buffer(), + owner_id.to_buffer(), + ) + } + } + TokenTransition::DirectPurchase(_) => { + if keeps_historical_document.keeps_direct_purchase_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + Drive::token_balance_for_identity_id_query( + token_id.to_buffer(), + owner_id.to_buffer(), + ) + } + } + TokenTransition::SetPriceForDirectPurchase(_) => { + if keeps_historical_document.keeps_direct_pricing_history() { + create_token_historical_document_query( + self, + owner_id, + platform_version, + )? + } else { + Drive::token_direct_purchase_price_query(token_id.to_buffer()) + } + } + TokenTransition::DestroyFrozenFunds(_) + | TokenTransition::EmergencyAction(_) + | TokenTransition::ConfigUpdate(_) + | TokenTransition::Claim(_) => create_token_historical_document_query( + self, + owner_id, + platform_version, + )?, + }; + + if let Some(group_state_transition_info) = self.base().using_group_info() { + // if we are using group info, one of two things might have happened + // either the action happened or it was initiated + // we need to merge the action success query and the query for group signers + let group_signer_query = create_token_group_action_query(self.data_contract_id().to_buffer(), owner_id.to_buffer(), group_state_transition_info); + let mut success_query = action_success_query; + // We need to remove the limit here + success_query.query.limit = None; + PathQuery::merge(vec![&group_signer_query, &success_query], &platform_version.drive.grove_version).map_err(Error::GroveDB) + + } else { + // if there is no group info that all we need is this query + Ok(action_success_query) + } + } +} \ No newline at end of file diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index f37805cbe35..6779628bea8 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -11,6 +11,7 @@ use dpp::document::{Document, DocumentV0Getters}; use dpp::document::document_methods::DocumentMethodsV0; use dpp::document::property_names::PRICE; use dpp::fee::Credits; +use dpp::group::group_action_status::GroupActionStatus; use dpp::identity::PartialIdentity; use dpp::platform_value::btreemap_extensions::BTreeValueMapHelper; use dpp::prelude::Identifier; @@ -40,7 +41,7 @@ use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_m use dpp::state_transition::batch_transition::token_unfreeze_transition::v0::v0_methods::TokenUnfreezeTransitionV0Methods; use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; use dpp::state_transition::proof_result::StateTransitionProofResult; -use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; +use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenGroupActionWithDocument, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; use dpp::system_data_contracts::{load_system_data_contract, SystemDataContract}; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; use dpp::voting::vote_polls::VotePoll; @@ -54,6 +55,7 @@ use crate::verify::RootHash; use crate::error::Error; use crate::error::proof::ProofError; use crate::query::{ContractLookupFn, SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus}; +use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; impl Drive { #[inline(always)] @@ -125,7 +127,7 @@ impl Drive { let contract = known_contracts_provider_fn(&data_contract_id)?.ok_or( Error::Proof(ProofError::UnknownContract(format!( - "unknown contract with id {}", + "unknown contract with id {} in document verification", data_contract_id ))), )?; @@ -295,13 +297,12 @@ impl Drive { } } BatchedTransitionRef::Token(token_transition) => { - //todo group actions let data_contract_id = token_transition.data_contract_id(); let token_id = token_transition.token_id(); let contract = known_contracts_provider_fn(&data_contract_id)?.ok_or( Error::Proof(ProofError::UnknownContract(format!( - "unknown contract with id {}", + "unknown contract with id {} in token verification", data_contract_id ))), )?; @@ -338,46 +339,56 @@ impl Drive { SingleDocumentDriveQueryContestedStatus::NotContested, }; - // println!("query used to verify {:?}", query); - // - // println!("proof bytes are {}", hex::encode(proof)); + let is_group_action = token_transition.base().using_group_info().is_some(); let (root_hash, document) = query.verify_proof( - false, + is_group_action, // it will be a subset if it is a group action proof, token_history_document_type, platform_version, )?; + + if let Some(document) = &document { + let expected_document = token_transition.build_historical_document( + token_id, + owner_id, + identity_contract_nonce, + &BlockInfo::default(), + token_config, + platform_version, + )?; - let document = document.ok_or(Error::Proof(ProofError::IncorrectProof(format!("proof did not contain document of type `{}` expected to exist because the token keeps historical documents", token_transition.historical_document_type_name()))))?; - - let expected_document = token_transition.build_historical_document( - token_id, - owner_id, - identity_contract_nonce, - &BlockInfo::default(), - token_config, - platform_version, - )?; + // Some fields are populated by the drive, + // so we need to ignore them + let ignore_fields = match token_transition { + TokenTransition::DestroyFrozenFunds(_) => { + Some(vec!["destroyedAmount"]) + } + TokenTransition::Claim(_) => Some(vec!["amount"]), + _ => None, + }; - // Some fields are populated by the drive, - // so we need to ignore them - let ignore_fields = match token_transition { - TokenTransition::DestroyFrozenFunds(_) => { - Some(vec!["destroyedAmount"]) + if !document.is_equal_ignoring_time_based_fields( + &expected_document, + ignore_fields, + platform_version, + )? { + return Err(Error::Proof(ProofError::UnexpectedResultProof(format!("proof of state transition execution did not show the correct historical document got: [{}] vs expected: [{}], state transition is [{}]", document, expected_document, token_transition)))); } - TokenTransition::Claim(_) => Some(vec!["amount"]), - _ => None, - }; - - if !document.is_equal_ignoring_time_based_fields( - &expected_document, - ignore_fields, - platform_version, - )? { - return Err(Error::Proof(ProofError::UnexpectedResultProof(format!("proof of state transition execution did not show the correct historical document got: [{}] vs expected: [{}], state transition is [{}]", document, expected_document, token_transition)))); } - Ok((root_hash, VerifiedTokenActionWithDocument(document))) + + if let Some(group_state_transition_info) = token_transition.base().using_group_info() { + let action_status = if document.is_some() { + GroupActionStatus::ActionClosed + } else { + GroupActionStatus::ActionActive + }; + let sum_power = Drive::verify_action_signer_and_total_power(proof, data_contract_id, group_state_transition_info.group_contract_position, action_status, group_state_transition_info.action_id, owner_id, true, platform_version)?.1; + Ok((root_hash, VerifiedTokenGroupActionWithDocument(sum_power, document))) + } else { + let document = document.ok_or(Error::Proof(ProofError::IncorrectProof(format!("proof did not contain document of type `{}` expected to exist because the token keeps historical documents", token_transition.historical_document_type_name()))))?; + Ok((root_hash, VerifiedTokenActionWithDocument(document))) + } }; match token_transition { TokenTransition::Burn(_) => { @@ -685,7 +696,7 @@ impl Drive { )? .ok_or(Error::Proof( ProofError::UnknownContract(format!( - "unknown contract with id {}", + "unknown contract with id {} in resource vote verification", contested_document_resource_vote_poll.contract_id )), ))?, diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 6ec3ec302b6..fcfaa68527e 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.0-rc.3" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "6d8ce92abe0c888f0a0115a6c88effa04ea87ed3" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } once_cell = "1.19.0" [features] 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 3346f1c2cf0..d881565bbff 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 @@ -52,6 +52,7 @@ pub struct DriveVerifyGroupMethodVersions { pub verify_group_infos_in_contract: FeatureVersion, pub verify_action_infos: FeatureVersion, pub verify_action_signers: FeatureVersion, + pub verify_action_signers_total_power: 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 72cd98d0a9a..cb76106391a 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 @@ -36,6 +36,7 @@ pub const DRIVE_VERIFY_METHOD_VERSIONS_V1: DriveVerifyMethodVersions = DriveVeri verify_group_infos_in_contract: 0, verify_action_infos: 0, verify_action_signers: 0, + verify_action_signers_total_power: 0, }, token: DriveVerifyTokenMethodVersions { verify_token_balances_for_identity_ids: 0, diff --git a/packages/rs-platform-version/src/version/system_limits/mod.rs b/packages/rs-platform-version/src/version/system_limits/mod.rs index 67f3fb83718..70b233de400 100644 --- a/packages/rs-platform-version/src/version/system_limits/mod.rs +++ b/packages/rs-platform-version/src/version/system_limits/mod.rs @@ -9,7 +9,7 @@ pub struct SystemLimits { pub withdrawal_transactions_per_block_limit: u16, pub retry_signing_expired_withdrawal_documents_per_block_limit: u16, pub max_withdrawal_amount: u64, - pub max_contract_group_size: u32, + pub max_contract_group_size: u16, // This the max redemption cycles we can process if we don't use a constant distribution // For a constant perpetual distribution this is very cheap since it's just a multiplication // For other distributions we much calculate at each cycle the rewards, so we don't want to From 091b1b813b20a943c54953914229ab44166e2dd6 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 12 May 2025 18:54:03 +0700 Subject: [PATCH 2/9] more fixes --- packages/rs-dpp/src/group/group_action/mod.rs | 28 + .../rs-dpp/src/group/group_action/v0/mod.rs | 19 + .../src/state_transition/proof_result.rs | 2 +- .../create_genesis_state/test/tokens.rs | 2 + .../batch/tests/token/mint/mod.rs | 548 +++++++++--------- .../for_add_group_action/v0/mod.rs | 10 +- .../v0/mod.rs | 6 +- .../fetch_action_id_signers_power/v0/mod.rs | 6 +- .../fetch/fetch_active_action_info/v0/mod.rs | 6 +- .../group/insert/add_group_action/v0/mod.rs | 190 +++--- packages/rs-drive/src/drive/group/paths.rs | 4 +- .../group/prove/prove_action_infos/v0/mod.rs | 18 + .../prove/prove_action_signers/v0/mod.rs | 15 +- packages/rs-drive/src/drive/group/queries.rs | 23 +- packages/rs-drive/src/error/drive.rs | 8 + packages/rs-drive/src/fees/op.rs | 35 +- .../prove/prove_state_transition/v0/mod.rs | 8 +- .../batch/token/token_burn_transition.rs | 2 + .../token/token_config_update_transition.rs | 2 + .../token_destroy_frozen_funds_transition.rs | 2 + .../token_emergency_action_transition.rs | 2 + .../batch/token/token_freeze_transition.rs | 2 + .../batch/token/token_mint_transition.rs | 2 + ...et_price_for_direct_purchase_transition.rs | 2 + .../batch/token/token_unfreeze_transition.rs | 2 + .../src/util/batch/drive_op_batch/group.rs | 2 +- .../grove_operations/batch_delete/v0/mod.rs | 2 +- .../util/grove_operations/batch_move/mod.rs | 45 ++ .../grove_operations/batch_move/v0/mod.rs | 334 +++++++++++ .../batch_move_items_in_path_query/v0/mod.rs | 4 +- .../rs-drive/src/util/grove_operations/mod.rs | 1 + packages/rs-drive/src/verify/group/mod.rs | 2 +- .../verify_action_signers_total_power/mod.rs | 2 +- .../v0/mod.rs | 19 +- .../src/verify/state_transition/mod.rs | 2 +- .../mod.rs | 4 +- .../token_transition.rs | 116 ++-- .../v0/mod.rs | 46 +- .../drive_grove_method_versions/mod.rs | 1 + .../drive_grove_method_versions/v1.rs | 1 + packages/rs-sdk/tests/fetch/group_actions.rs | 4 +- 41 files changed, 1039 insertions(+), 490 deletions(-) create mode 100644 packages/rs-drive/src/util/grove_operations/batch_move/mod.rs create mode 100644 packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs diff --git a/packages/rs-dpp/src/group/group_action/mod.rs b/packages/rs-dpp/src/group/group_action/mod.rs index 7e39ab631cf..efbba5665b9 100644 --- a/packages/rs-dpp/src/group/group_action/mod.rs +++ b/packages/rs-dpp/src/group/group_action/mod.rs @@ -1,9 +1,12 @@ pub mod v0; +use crate::data_contract::TokenContractPosition; +use crate::group::action_event::GroupActionEvent; use crate::group::group_action::v0::GroupActionV0; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; #[derive( Debug, PartialEq, PartialOrd, Clone, Eq, Encode, Decode, PlatformDeserialize, PlatformSerialize, @@ -12,3 +15,28 @@ use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; pub enum GroupAction { V0(GroupActionV0), } + +pub trait GroupActionAccessors { + fn contract_id(&self) -> Identifier; + fn token_contract_position(&self) -> TokenContractPosition; + fn event(&self) -> &GroupActionEvent; +} +impl GroupActionAccessors for GroupAction { + fn contract_id(&self) -> Identifier { + match self { + GroupAction::V0(inner) => inner.contract_id(), + } + } + + fn token_contract_position(&self) -> TokenContractPosition { + match self { + GroupAction::V0(inner) => inner.token_contract_position(), + } + } + + fn event(&self) -> &GroupActionEvent { + match self { + GroupAction::V0(inner) => inner.event(), + } + } +} diff --git a/packages/rs-dpp/src/group/group_action/v0/mod.rs b/packages/rs-dpp/src/group/group_action/v0/mod.rs index 5f90ce0788b..ad624d361f7 100644 --- a/packages/rs-dpp/src/group/group_action/v0/mod.rs +++ b/packages/rs-dpp/src/group/group_action/v0/mod.rs @@ -1,12 +1,31 @@ +use crate::data_contract::TokenContractPosition; use crate::group::action_event::GroupActionEvent; +use crate::group::group_action::GroupActionAccessors; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; #[derive( Debug, PartialEq, PartialOrd, Clone, Eq, Encode, Decode, PlatformDeserialize, PlatformSerialize, )] #[platform_serialize(unversioned)] //versioned directly, no need to use platform_version pub struct GroupActionV0 { + pub contract_id: Identifier, + pub token_contract_position: TokenContractPosition, pub event: GroupActionEvent, } + +impl GroupActionAccessors for GroupActionV0 { + fn contract_id(&self) -> Identifier { + self.contract_id + } + + fn token_contract_position(&self) -> TokenContractPosition { + self.token_contract_position + } + + fn event(&self) -> &GroupActionEvent { + &self.event + } +} diff --git a/packages/rs-dpp/src/state_transition/proof_result.rs b/packages/rs-dpp/src/state_transition/proof_result.rs index b8fb9c6e1dc..7be9a4a6dd7 100644 --- a/packages/rs-dpp/src/state_transition/proof_result.rs +++ b/packages/rs-dpp/src/state_transition/proof_result.rs @@ -1,4 +1,5 @@ use crate::balances::credits::TokenAmount; +use crate::data_contract::group::GroupSumPower; use crate::data_contract::DataContract; use crate::document::Document; use crate::identity::{Identity, PartialIdentity}; @@ -8,7 +9,6 @@ use crate::tokens::token_pricing_schedule::TokenPricingSchedule; use crate::voting::votes::Vote; use platform_value::Identifier; use std::collections::BTreeMap; -use crate::data_contract::group::GroupSumPower; #[derive(Debug, strum::Display, derive_more::TryInto)] pub enum StateTransitionProofResult { diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs index 8c36df1c828..c6239b862fc 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs @@ -88,6 +88,8 @@ impl Platform { // Add burn token group action let action_id = Identifier::new([32; 32]); let group_action = GroupAction::V0(GroupActionV0 { + contract_id: DATA_CONTRACT_ID, + token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some("world on fire".into()))), }); diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index ce79e4a7d06..81e05d68668 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -45,12 +45,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -68,9 +67,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -131,12 +130,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -154,9 +152,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -219,12 +217,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -242,12 +239,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), + _ + )] + ); platform .drive @@ -283,8 +280,7 @@ mod token_mint_tests { let (identity, signer, key) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (receiver, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); let (contract, token_id) = create_token_contract_with_owner_identity( &mut platform, @@ -311,12 +307,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -334,9 +329,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -399,12 +394,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -422,14 +416,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::RecipientIdentityDoesNotExistError( - _ - )), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::RecipientIdentityDoesNotExistError(_)), + _ + )] + ); platform .drive @@ -490,12 +482,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -513,14 +504,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::DestinationIdentityForTokenMintingNotSetError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::DestinationIdentityForTokenMintingNotSetError(_) + ), + _ + )] + ); platform .drive @@ -578,12 +569,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -601,14 +591,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); platform .drive @@ -644,8 +634,7 @@ mod token_mint_tests { let (identity, signer, key) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (receiver, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); let (contract, token_id) = create_token_contract_with_owner_identity( &mut platform, @@ -676,12 +665,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -699,14 +687,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); platform .drive @@ -771,12 +759,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -794,14 +781,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::DestinationIdentityForTokenMintingNotSetError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::DestinationIdentityForTokenMintingNotSetError(_) + ), + _ + )] + ); platform .drive @@ -862,12 +849,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -885,14 +871,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); platform .drive @@ -928,8 +914,7 @@ mod token_mint_tests { let (identity, signer, key) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - let (receiver, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (receiver, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); let (contract, token_id) = create_token_contract_with_owner_identity( &mut platform, @@ -963,12 +948,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -986,14 +970,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::BasicError( - BasicError::ChoosingTokenMintRecipientNotAllowedError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::BasicError( + BasicError::ChoosingTokenMintRecipientNotAllowedError(_) + ), + _ + )] + ); platform .drive @@ -1061,12 +1045,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -1084,9 +1067,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -1109,9 +1092,8 @@ mod token_mint_tests { } mod token_mint_tests_authorization_scenarios { - use std::collections::BTreeMap; - use dpp::data_contract::associated_token::token_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Setters; use super::*; + use dpp::data_contract::associated_token::token_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Setters; use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; use dpp::data_contract::change_control_rules::ChangeControlRules; @@ -1172,12 +1154,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -1195,12 +1176,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); platform .drive @@ -1262,7 +1243,7 @@ mod token_mint_tests { required_power: 10, }), )] - .into(), + .into(), ), platform_version, ); @@ -1283,12 +1264,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -1306,12 +1286,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); platform .drive @@ -1375,7 +1355,7 @@ mod token_mint_tests { required_power: 5, }), )] - .into(), + .into(), ), platform_version, ); @@ -1396,12 +1376,11 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let documents_batch_create_serialized_transition = - documents_batch_create_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let documents_batch_create_serialized_transition = documents_batch_create_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -1419,9 +1398,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -1451,7 +1430,7 @@ mod token_mint_tests { fn test_token_mint_by_owner_requires_group_other_member_no_history() { test_token_mint_by_owner_requires_group_other_member(false); } - + fn test_token_mint_by_owner_requires_group_other_member(keeps_minting_history: bool) { // We are using a group, and two members need to sign for the event to happen let platform_version = PlatformVersion::latest(); @@ -1474,7 +1453,9 @@ mod token_mint_tests { &mut platform, identity.id(), Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.keeps_history_mut().set_keeps_minting_history(keeps_minting_history); + token_configuration + .keeps_history_mut() + .set_keeps_minting_history(keeps_minting_history); token_configuration.set_manual_minting_rules(ChangeControlRules::V0( ChangeControlRulesV0 { authorized_to_make_change: AuthorizedActionTakers::Group(0), @@ -1494,7 +1475,7 @@ mod token_mint_tests { required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -1515,7 +1496,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -1537,9 +1518,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -1547,17 +1528,29 @@ mod token_mint_tests { .commit_transaction(transaction) .unwrap() .expect("expected to commit transaction"); - + // Let's verify the proof of the state transition - - let proof = platform.drive.prove_state_transition(&token_mint_transition, None, platform_version).expect("expect to prove state transition"); - - let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof(&token_mint_transition, &BlockInfo::default(), proof.data.as_ref().expect("expected data"), &|_| { - Ok(Some(contract.clone().into())) - }, - platform_version, - ).expect(format!("expect to verify state transition proof {}", hex::encode(&proof.data.expect("expected data"))).as_str()); - + + let proof = platform + .drive + .prove_state_transition(&token_mint_transition, None, platform_version) + .expect("expect to prove state transition"); + + let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof( + &token_mint_transition, + &BlockInfo::default(), + proof.data.as_ref().expect("expected data"), + &|_| Ok(Some(contract.clone().into())), + platform_version, + ) + .expect( + format!( + "expect to verify state transition proof {}", + hex::encode(&proof.data.expect("expected data")) + ) + .as_str(), + ); + if keeps_minting_history { assert_matches!( result, @@ -1566,8 +1559,8 @@ mod token_mint_tests { assert_eq!(doc, None); } ); - } else { - assert_matches!( + } else { + assert_matches!( result, StateTransitionProofResult::VerifiedTokenBalance(id, amount) => { assert_eq!(id, identity.id()); @@ -1618,7 +1611,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -1640,9 +1633,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -1716,7 +1709,7 @@ mod token_mint_tests { required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -1737,7 +1730,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -1759,9 +1752,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -1813,7 +1806,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -1835,14 +1828,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError( - StateError::GroupActionAlreadySignedByIdentityError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError( + StateError::GroupActionAlreadySignedByIdentityError(_) + ), + _ + )] + ); platform .drive @@ -1920,11 +1913,11 @@ mod token_mint_tests { (identity_2.id(), 1), (identity_3.id(), 1), ] - .into(), + .into(), required_power: 3, }), )] - .into(), + .into(), ), platform_version, ); @@ -1945,7 +1938,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -1967,9 +1960,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -2020,7 +2013,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2042,9 +2035,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -2100,7 +2093,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2122,14 +2115,14 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError( - StateError::GroupActionAlreadySignedByIdentityError(_) - ), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError( + StateError::GroupActionAlreadySignedByIdentityError(_) + ), + _ + )] + ); platform .drive @@ -2208,11 +2201,11 @@ mod token_mint_tests { (identity_2.id(), 1), (identity_3.id(), 1), ] - .into(), + .into(), required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -2233,7 +2226,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -2255,9 +2248,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -2308,7 +2301,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2330,9 +2323,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -2388,7 +2381,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2410,12 +2403,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::GroupActionAlreadyCompletedError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::GroupActionAlreadyCompletedError(_)), + _ + )] + ); platform .drive @@ -2492,7 +2485,7 @@ mod token_mint_tests { required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -2513,7 +2506,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -2535,12 +2528,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), + _ + )] + ); platform .drive @@ -2606,7 +2599,7 @@ mod token_mint_tests { required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -2627,7 +2620,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let token_mint_serialized_transition = token_mint_transition .serialize_to_bytes() @@ -2649,9 +2642,9 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -2702,7 +2695,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2724,12 +2717,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityNotMemberOfGroupError(_)), + _ + )] + ); platform .drive @@ -2774,8 +2767,7 @@ mod token_mint_tests { let platform_state = platform.state.load(); - let (identity, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (identity, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); let (identity_2, signer2, key2) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); @@ -2803,7 +2795,7 @@ mod token_mint_tests { required_power: 2, }), )] - .into(), + .into(), ), platform_version, ); @@ -2839,7 +2831,7 @@ mod token_mint_tests { platform_version, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let confirm_token_mint_serialized_transition = confirm_token_mint_transition .serialize_to_bytes() @@ -2861,12 +2853,12 @@ mod token_mint_tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [PaidConsensusError( - ConsensusError::StateError(StateError::GroupActionDoesNotExistError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::GroupActionDoesNotExistError(_)), + _ + )] + ); platform .drive @@ -2898,4 +2890,4 @@ mod token_mint_tests { assert_eq!(token_balance, None); } } -} \ No newline at end of file +} diff --git a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs index 3546944698a..28e549fa067 100644 --- a/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/estimated_costs/for_add_group_action/v0/mod.rs @@ -1,6 +1,10 @@ use crate::drive::Drive; -use crate::drive::group::paths::{group_action_path, group_action_signers_path, group_active_action_root_path, group_closed_action_path, group_closed_action_root_path, group_closed_action_signers_path, group_contract_path, group_path, group_root_path}; +use crate::drive::group::paths::{ + group_action_signers_path, group_active_action_path, group_active_action_root_path, + group_closed_action_path, group_closed_action_root_path, group_closed_action_signers_path, + group_contract_path, group_path, group_root_path, +}; use crate::util::type_constants::DEFAULT_HASH_SIZE_U8; use dpp::data_contract::GroupContractPosition; use grovedb::batch::KeyInfoPath; @@ -137,7 +141,7 @@ impl Drive { if let Some(action_id) = action_id { estimated_costs_only_with_layer_info.insert( - KeyInfoPath::from_known_path(group_action_path( + KeyInfoPath::from_known_path(group_active_action_path( contract_id.as_slice(), &group_contract_position.to_be_bytes(), action_id.as_slice(), @@ -166,7 +170,7 @@ impl Drive { }, ); } - + if also_add_closed_tree { estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(group_closed_action_root_path( diff --git a/packages/rs-drive/src/drive/group/fetch/fetch_action_id_info_keep_serialized/v0/mod.rs b/packages/rs-drive/src/drive/group/fetch/fetch_action_id_info_keep_serialized/v0/mod.rs index 9b5a14d4d72..bf38338611b 100644 --- a/packages/rs-drive/src/drive/group/fetch/fetch_action_id_info_keep_serialized/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/fetch/fetch_action_id_info_keep_serialized/v0/mod.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::drive::group::paths::{group_action_path, ACTION_INFO_KEY}; +use crate::drive::group::paths::{group_active_action_path, ACTION_INFO_KEY}; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; @@ -46,7 +46,7 @@ impl Drive { ) -> Result, Error> { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), @@ -105,7 +105,7 @@ impl Drive { ) -> Result, Error> { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), diff --git a/packages/rs-drive/src/drive/group/fetch/fetch_action_id_signers_power/v0/mod.rs b/packages/rs-drive/src/drive/group/fetch/fetch_action_id_signers_power/v0/mod.rs index 078c4c2c8b3..76d13ea390b 100644 --- a/packages/rs-drive/src/drive/group/fetch/fetch_action_id_signers_power/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/fetch/fetch_action_id_signers_power/v0/mod.rs @@ -1,4 +1,4 @@ -use crate::drive::group::paths::{group_action_path, ACTION_SIGNERS_KEY}; +use crate::drive::group::paths::{group_active_action_path, ACTION_SIGNERS_KEY}; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; @@ -44,7 +44,7 @@ impl Drive { ) -> Result, Error> { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), @@ -101,7 +101,7 @@ impl Drive { ) -> Result, Error> { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), diff --git a/packages/rs-drive/src/drive/group/fetch/fetch_active_action_info/v0/mod.rs b/packages/rs-drive/src/drive/group/fetch/fetch_active_action_info/v0/mod.rs index b1f9d668c75..2c0da3718a8 100644 --- a/packages/rs-drive/src/drive/group/fetch/fetch_active_action_info/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/fetch/fetch_active_action_info/v0/mod.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::drive::group::paths::{group_action_path, ACTION_INFO_KEY}; +use crate::drive::group::paths::{group_active_action_path, ACTION_INFO_KEY}; use crate::drive::Drive; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; @@ -25,7 +25,7 @@ impl Drive { ) -> Result { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), @@ -62,7 +62,7 @@ impl Drive { ) -> Result { let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); // Construct the GroveDB path for the action signers - let path = group_action_path( + let path = group_active_action_path( contract_id.as_ref(), &group_contract_position_bytes, action_id.as_ref(), diff --git a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs index b55b45e7382..b49959ab9c0 100644 --- a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs @@ -1,8 +1,15 @@ -use crate::drive::group::paths::{group_action_path, group_action_signers_path_vec, group_active_action_root_path, group_closed_action_path, group_closed_action_root_path, group_closed_action_signers_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY}; +use crate::drive::group::paths::{ + group_action_signers_path_vec, group_active_action_path, group_active_action_root_path, + group_closed_action_path, group_closed_action_path_vec, group_closed_action_root_path, + group_closed_action_signers_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY, +}; use crate::drive::Drive; +use crate::error::drive::DriveError; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; -use crate::util::grove_operations::{BatchInsertApplyType, BatchInsertTreeApplyType, BatchMoveApplyType, QueryTarget}; +use crate::util::grove_operations::{ + BatchInsertApplyType, BatchInsertTreeApplyType, BatchMoveApplyType, QueryTarget, +}; use crate::util::object_size_info::PathKeyInfo::PathFixedSizeKeyRef; use crate::util::object_size_info::{DriveKeyInfo, PathKeyElementInfo}; use dpp::block::block_info::BlockInfo; @@ -10,16 +17,16 @@ use dpp::data_contract::group::GroupMemberPower; use dpp::data_contract::GroupContractPosition; use dpp::fee::fee_result::FeeResult; use dpp::group::group_action::GroupAction; +use dpp::group::group_action_status::GroupActionStatus; use dpp::identifier::Identifier; use dpp::serialization::PlatformSerializable; use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::element::SumValue; +use grovedb::MaybeTree::NotTree; use grovedb::{Element, EstimatedLayerInformation, TransactionArg, TreeType}; use grovedb_epoch_based_storage_flags::StorageFlags; use std::collections::HashMap; -use grovedb::MaybeTree::NotTree; -use dpp::group::group_action_status::GroupActionStatus; impl Drive { #[allow(clippy::too_many_arguments)] @@ -141,8 +148,10 @@ impl Drive { } let group_contract_position_bytes = group_contract_position.to_be_bytes().to_vec(); - + if !closes_group_action { + // We are not closing the group action, this means for example that the required power + // is 5, and we are getting to 2. let group_action_root_path = group_active_action_root_path( contract_id.as_slice(), group_contract_position_bytes.as_slice(), @@ -180,7 +189,7 @@ impl Drive { )?; if inserted_root_action { - let group_action_path = group_action_path( + let group_action_path = group_active_action_path( contract_id.as_slice(), group_contract_position_bytes.as_slice(), action_id.as_slice(), @@ -194,7 +203,8 @@ impl Drive { &platform_version.drive, )?; - let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + let serialized = + initialize_with_insert_action_info.serialize_consume_to_bytes()?; self.batch_insert( PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( @@ -250,6 +260,7 @@ impl Drive { } } else { // We are closing the group action + // This means for example that the required power is 5, and we are getting to 5 or above. let group_action_root_path = group_closed_action_root_path( contract_id.as_slice(), group_contract_position_bytes.as_slice(), @@ -264,49 +275,35 @@ impl Drive { } }; - let mut inserted_root_action = false; - - if let Some(initialize_with_insert_action_info) = initialize_with_insert_action_info { - // We insert the contract root into the group tree - inserted_root_action = self.batch_insert_empty_tree_if_not_exists( - PathFixedSizeKeyRef((group_action_root_path, action_id.as_slice())), - TreeType::NormalTree, - None, - apply_type, - transaction, - &mut None, - &mut batch_operations, - &platform_version.drive, - )?; - - if inserted_root_action { - let group_action_path = group_closed_action_path( - contract_id.as_slice(), - group_contract_position_bytes.as_slice(), - action_id.as_slice(), - ); + let inserted = self.batch_insert_empty_tree_if_not_exists( + PathFixedSizeKeyRef((group_action_root_path, action_id.as_slice())), + TreeType::NormalTree, + None, + apply_type, + transaction, + &mut None, + &mut batch_operations, + &platform_version.drive, + )?; - self.batch_insert_empty_sum_tree( - group_action_path, - DriveKeyInfo::KeyRef(ACTION_SIGNERS_KEY), - None, - &mut batch_operations, - &platform_version.drive, - )?; + if !inserted { + // This is an error because we should always be closing things + return Err(Error::Drive(DriveError::InvalidAction("inserting a group action as closed should always create a new closed action tree for that action id"))); + } - let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + let group_closed_action_path = group_closed_action_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + action_id.as_slice(), + ); - self.batch_insert( - PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( - group_action_path, - ACTION_INFO_KEY, - Element::Item(serialized, None), - )), - &mut batch_operations, - &platform_version.drive, - )?; - } - } + self.batch_insert_empty_sum_tree( + group_closed_action_path, + DriveKeyInfo::KeyRef(ACTION_SIGNERS_KEY), + None, + &mut batch_operations, + &platform_version.drive, + )?; let closed_signers_path = group_closed_action_signers_path_vec( contract_id.as_slice(), @@ -314,41 +311,15 @@ impl Drive { action_id.as_slice(), ); - let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { - BatchInsertApplyType::StatefulBatchInsert - } else { - BatchInsertApplyType::StatelessBatchInsert { - in_tree_type: TreeType::SumTree, - target: QueryTarget::QueryTargetValue(8), - } - }; - - if inserted_root_action { - self.batch_insert( - PathKeyElementInfo::PathKeyElement::<0>(( - closed_signers_path.clone(), - signer_identity_id.to_vec(), - Element::SumItem(signer_power as SumValue, None), - )), - &mut batch_operations, - &platform_version.drive, - )?; - } else { - // we should verify it doesn't yet exist - self.batch_insert_sum_item_if_not_exists( - PathKeyElementInfo::PathKeyElement::<0>(( - closed_signers_path.clone(), - signer_identity_id.to_vec(), - Element::SumItem(signer_power as SumValue, None), - )), - true, - signer_apply_type, - transaction, - &mut batch_operations, - &platform_version.drive, - )?; - } - + self.batch_insert( + PathKeyElementInfo::PathKeyElement::<0>(( + closed_signers_path.clone(), + signer_identity_id.to_vec(), + Element::SumItem(signer_power as SumValue, None), + )), + &mut batch_operations, + &platform_version.drive, + )?; // now we need to also move all signers from the active tree to the closed tree let mut active_path_query = Drive::group_action_signers_query( @@ -358,7 +329,8 @@ impl Drive { action_id.to_buffer(), ); - active_path_query.query.limit = Some(platform_version.system_limits.max_contract_group_size); + active_path_query.query.limit = + Some(platform_version.system_limits.max_contract_group_size); let signer_apply_type = if estimated_costs_only_with_layer_info.is_none() { BatchMoveApplyType::StatefulBatchMove { @@ -384,6 +356,58 @@ impl Drive { &mut batch_operations, &platform_version.drive, )?; + + if let Some(initialize_with_insert_action_info) = initialize_with_insert_action_info { + let serialized = initialize_with_insert_action_info.serialize_consume_to_bytes()?; + + self.batch_insert( + PathKeyElementInfo::PathFixedSizeKeyRefElement::<5>(( + group_closed_action_path, + ACTION_INFO_KEY, + Element::Item(serialized, None), + )), + &mut batch_operations, + &platform_version.drive, + )?; + } else { + // we should move the info from the active state to the closed state + let info_move_apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum: Some(NotTree), + } + } else { + BatchMoveApplyType::StatelessBatchMove { + in_tree_type: TreeType::NormalTree, + tree_type: None, + estimated_key_size: 5, // fixed‑size “info” key + estimated_value_size: 0, // value size unknown / not critical + flags_len: 0, + } + }; + + let group_active_action_path = group_active_action_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + action_id.as_slice(), + ); + + let group_closed_action_path_vec = group_closed_action_path_vec( + contract_id.as_slice(), + group_contract_position, + action_id.as_slice(), + ); + + self.batch_move( + group_active_action_path.as_slice().into(), // from active path + ACTION_INFO_KEY, // key to move + group_closed_action_path_vec, // to closed path + info_move_apply_type, + Some(None), // no flags on the closed item + transaction, + &mut batch_operations, + &platform_version.drive, + )?; + } } Ok(batch_operations) diff --git a/packages/rs-drive/src/drive/group/paths.rs b/packages/rs-drive/src/drive/group/paths.rs index 31ae870a12b..b498fcadaa7 100644 --- a/packages/rs-drive/src/drive/group/paths.rs +++ b/packages/rs-drive/src/drive/group/paths.rs @@ -88,7 +88,7 @@ pub fn group_active_action_root_path_vec( } /// Group path -pub fn group_action_path<'a>( +pub fn group_active_action_path<'a>( contract_id: &'a [u8], group_contract_position_bytes: &'a [u8], action_id: &'a [u8], @@ -103,7 +103,7 @@ pub fn group_action_path<'a>( } /// Group path vector -pub fn group_action_path_vec( +pub fn group_active_action_path_vec( contract_id: &[u8], group_contract_position: GroupContractPosition, action_id: &[u8], diff --git a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs index b909d49d093..d6b203549fa 100644 --- a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs @@ -176,10 +176,14 @@ mod tests { let action_id_2 = Identifier::random(); let action_1 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(100, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), }); @@ -189,6 +193,7 @@ mod tests { contract_id, group_contract_position, Some(action_1.clone()), + false, action_id_1, identity_1_id, 1, @@ -204,6 +209,7 @@ mod tests { contract_id, group_contract_position, Some(action_2.clone()), + true, action_id_2, identity_2_id, 1, @@ -407,14 +413,20 @@ mod tests { let action_id_4 = Identifier::new([4; 32]); let action_1 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(200, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), }); let action_3 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Transfer( identity_2_id, None, @@ -425,6 +437,8 @@ mod tests { }); let action_4 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Transfer( identity_3_id, None, @@ -440,6 +454,7 @@ mod tests { contract_id, group_contract_position, Some(action_1.clone()), + false, action_id_1, identity_1_id, 1, @@ -455,6 +470,7 @@ mod tests { contract_id, group_contract_position, Some(action_2.clone()), + false, action_id_2, identity_1_id, 1, @@ -470,6 +486,7 @@ mod tests { contract_id, group_contract_position, Some(action_3.clone()), + false, action_id_3, identity_2_id, 1, @@ -485,6 +502,7 @@ mod tests { contract_id, group_contract_position, Some(action_4.clone()), + false, action_id_4, identity_3_id, 1, diff --git a/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs b/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs index c8602ac7dd4..3a5aa1ede7d 100644 --- a/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/prove/prove_action_signers/v0/mod.rs @@ -125,7 +125,8 @@ mod tests { ( 0, Group::V0(GroupV0 { - members: [(identity_1_id, 1), (identity_2_id, 1)].into(), + members: [(identity_1_id, 1), (identity_2_id, 1), (identity_3_id, 1)] + .into(), required_power: 2, }), ), @@ -134,7 +135,7 @@ mod tests { Group::V0(GroupV0 { members: [(identity_1_id, 2), (identity_2_id, 1), (identity_3_id, 1)] .into(), - required_power: 2, + required_power: 3, }), ), ]), @@ -171,10 +172,14 @@ mod tests { let action_id_2 = Identifier::random(); let action_1 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Mint(100, identity_1_id, None)), }); let action_2 = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 1, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), }); @@ -184,6 +189,7 @@ mod tests { contract_id, group_contract_position, Some(action_1.clone()), + false, action_id_1, identity_1_id, 1, @@ -199,6 +205,7 @@ mod tests { contract_id, group_contract_position, Some(action_1.clone()), + false, action_id_1, identity_2_id, 1, @@ -214,6 +221,7 @@ mod tests { contract_id, group_contract_position, Some(action_2.clone()), + false, action_id_2, identity_2_id, 1, @@ -434,6 +442,8 @@ mod tests { // Create a closed action let action_id = Identifier::random(); let action = GroupAction::V0(GroupActionV0 { + contract_id, + token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(50, None)), }); @@ -442,6 +452,7 @@ mod tests { contract_id, group_contract_position, Some(action.clone()), + false, action_id, identity_2_id, 1, diff --git a/packages/rs-drive/src/drive/group/queries.rs b/packages/rs-drive/src/drive/group/queries.rs index 2a84cac6dd6..2630d2f6e91 100644 --- a/packages/rs-drive/src/drive/group/queries.rs +++ b/packages/rs-drive/src/drive/group/queries.rs @@ -1,4 +1,8 @@ -use crate::drive::group::paths::{group_action_signers_path_vec, group_closed_action_signers_path_vec, group_contract_path_vec, group_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY, GROUP_ACTIVE_ACTIONS_KEY, GROUP_CLOSED_ACTIONS_KEY, GROUP_INFO_KEY}; +use crate::drive::group::paths::{ + group_action_signers_path_vec, group_closed_action_signers_path_vec, group_contract_path_vec, + group_path_vec, ACTION_INFO_KEY, ACTION_SIGNERS_KEY, GROUP_ACTIVE_ACTIONS_KEY, + GROUP_CLOSED_ACTIONS_KEY, GROUP_INFO_KEY, +}; use crate::drive::Drive; use crate::query::{Query, QueryItem}; use dpp::data_contract::GroupContractPosition; @@ -153,7 +157,8 @@ impl Drive { action_id: [u8; 32], identity_id: [u8; 32], ) -> PathQuery { - let group_actions_path = group_action_signers_path_vec(&contract_id, group_contract_position, &action_id); + let group_actions_path = + group_action_signers_path_vec(&contract_id, group_contract_position, &action_id); let query = Query::new_single_key(identity_id.to_vec()); PathQuery { @@ -173,7 +178,8 @@ impl Drive { action_id: [u8; 32], identity_id: [u8; 32], ) -> PathQuery { - let group_actions_path = group_closed_action_signers_path_vec(&contract_id, group_contract_position, &action_id); + let group_actions_path = + group_closed_action_signers_path_vec(&contract_id, group_contract_position, &action_id); let query = Query::new_single_key(identity_id.to_vec()); PathQuery { @@ -226,8 +232,15 @@ impl Drive { ) -> PathQuery { let group_path = group_path_vec(&contract_id, group_contract_position); let mut query = Query::new_with_direction(true); - query.insert_keys(vec![GROUP_ACTIVE_ACTIONS_KEY.to_vec(), GROUP_CLOSED_ACTIONS_KEY.to_vec()]); - query.set_subquery_path(vec![action_id.to_vec(), ACTION_SIGNERS_KEY.to_vec(), identity_id.to_vec()]); + query.insert_keys(vec![ + GROUP_ACTIVE_ACTIONS_KEY.to_vec(), + GROUP_CLOSED_ACTIONS_KEY.to_vec(), + ]); + query.set_subquery_path(vec![ + action_id.to_vec(), + ACTION_SIGNERS_KEY.to_vec(), + identity_id.to_vec(), + ]); PathQuery { path: group_path, diff --git a/packages/rs-drive/src/error/drive.rs b/packages/rs-drive/src/error/drive.rs index 3ea8f641f4e..48402e06ca5 100644 --- a/packages/rs-drive/src/error/drive.rs +++ b/packages/rs-drive/src/error/drive.rs @@ -193,4 +193,12 @@ pub enum DriveError { /// Data Contract not found #[error("data contract does not have a start moment: {0}")] ContractDoesNotHaveAStartMoment(Identifier), + + /// Invalid action + #[error("invalid action: {0}")] + InvalidAction(&'static str), + + /// Element was not found + #[error("element not found: {0}")] + ElementNotFound(&'static str), } diff --git a/packages/rs-drive/src/fees/op.rs b/packages/rs-drive/src/fees/op.rs index e3bfd61c575..98319950103 100644 --- a/packages/rs-drive/src/fees/op.rs +++ b/packages/rs-drive/src/fees/op.rs @@ -3,6 +3,7 @@ use grovedb_costs::storage_cost::removal::Identifier; use grovedb_costs::storage_cost::removal::StorageRemovedBytes::{ BasicStorageRemoval, NoStorageRemoval, SectionedStorageRemoval, }; +use std::collections::BTreeMap; use enum_map::Enum; use grovedb::batch::key_info::KeyInfo; @@ -239,20 +240,32 @@ impl LowLevelDriveOperation { (FeeRefunds::default(), amount) } SectionedStorageRemoval(mut removal_per_epoch_by_identifier) => { - let previous_fee_versions = previous_fee_versions.ok_or(Error::Drive(DriveError::CorruptedCodeExecution("expected previous epoch index fee versions to be able to offer refunds")))?; + let system_amount = removal_per_epoch_by_identifier .remove(&Identifier::default()) .map_or(0, |a| a.values().sum()); - - ( - FeeRefunds::from_storage_removal( - removal_per_epoch_by_identifier, - epoch.index, - epochs_per_era, - previous_fee_versions, - )?, - system_amount, - ) + if fee_version.fee_version_number == 1 { + ( + FeeRefunds::from_storage_removal( + removal_per_epoch_by_identifier, + epoch.index, + epochs_per_era, + &BTreeMap::default(), + )?, + system_amount, + ) + } else { + let previous_fee_versions = previous_fee_versions.ok_or(Error::Drive(DriveError::CorruptedCodeExecution("expected previous epoch index fee versions to be able to offer refunds")))?; + ( + FeeRefunds::from_storage_removal( + removal_per_epoch_by_identifier, + epoch.index, + epochs_per_era, + previous_fee_versions, + )?, + system_amount, + ) + } } }; Ok(FeeResult { diff --git a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs index 955d8fc0eb1..1a48d91db9e 100644 --- a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs +++ b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs @@ -1,4 +1,3 @@ -use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; use crate::drive::Drive; use crate::error::proof::ProofError; use crate::error::Error; @@ -6,6 +5,7 @@ use crate::prove::prove_state_transition::ProofCreationResult; use crate::query::{ IdentityBasedVoteDriveQuery, SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus, }; +use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contracts::SystemDataContract; @@ -171,7 +171,11 @@ impl Drive { }; let contract = &contract_fetch_info.contract; - token_transition.try_transition_into_path_query_with_contract(contract, owner_id, platform_version)? + token_transition.try_transition_into_path_query_with_contract( + contract, + owner_id, + platform_version, + )? } } } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs index b266e5ca9c3..9e3b26be8e5 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_burn_transition.rs @@ -56,6 +56,8 @@ impl DriveHighLevelBatchOperationConverter for TokenBurnTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs index 6b305eacbab..4a4849e28d7 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs @@ -60,6 +60,8 @@ impl DriveHighLevelBatchOperationConverter for TokenConfigUpdateTransitionAction let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs index 46060cb63cb..f099c36bb86 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_destroy_frozen_funds_transition.rs @@ -55,6 +55,8 @@ impl DriveHighLevelBatchOperationConverter for TokenDestroyFrozenFundsTransition let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs index 120d5245421..424cab2afdc 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_emergency_action_transition.rs @@ -55,6 +55,8 @@ impl DriveHighLevelBatchOperationConverter for TokenEmergencyActionTransitionAct let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs index 602a2c88265..6d9c97dbf67 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_freeze_transition.rs @@ -59,6 +59,8 @@ impl DriveHighLevelBatchOperationConverter for TokenFreezeTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs index 08973a2cb17..14672d15c5a 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_mint_transition.rs @@ -60,6 +60,8 @@ impl DriveHighLevelBatchOperationConverter for TokenMintTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs index 60db43c86ea..117df8fac4b 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_set_price_for_direct_purchase_transition.rs @@ -59,6 +59,8 @@ impl DriveHighLevelBatchOperationConverter for TokenSetPriceForDirectPurchaseTra let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs index 3a7da4b0490..a9bd1787069 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_unfreeze_transition.rs @@ -59,6 +59,8 @@ impl DriveHighLevelBatchOperationConverter for TokenUnfreezeTransitionAction { let initialize_with_insert_action_info = if *action_is_proposer { Some(GroupAction::V0(GroupActionV0 { + contract_id: self.base().data_contract_id(), + token_contract_position: self.base().token_position(), event: GroupActionEvent::TokenEvent(event), })) } else { diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/group.rs b/packages/rs-drive/src/util/batch/drive_op_batch/group.rs index 51ca801b4af..aeedd267139 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/group.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/group.rs @@ -30,7 +30,7 @@ pub enum GroupOperationType { /// The signer's power in the group signer_power: GroupMemberPower, /// Should we close the group action and mark it as complete? - closes_group_action: bool + closes_group_action: bool, }, } diff --git a/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs index bc774456306..7b28657228c 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs @@ -13,7 +13,7 @@ use platform_version::version::drive_versions::DriveVersion; impl Drive { /// Pushes a "delete element" operation to `drive_operations`. - pub(crate) fn batch_delete_v0>( + pub(super) fn batch_delete_v0>( &self, path: SubtreePath<'_, B>, key: &[u8], diff --git a/packages/rs-drive/src/util/grove_operations/batch_move/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move/mod.rs new file mode 100644 index 00000000000..eb8b64d8214 --- /dev/null +++ b/packages/rs-drive/src/util/grove_operations/batch_move/mod.rs @@ -0,0 +1,45 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::util::grove_operations::BatchMoveApplyType; +use dpp::version::drive_versions::DriveVersion; +use grovedb::TransactionArg; +use grovedb_epoch_based_storage_flags::StorageFlags; +use grovedb_path::SubtreePath; + +impl Drive { + /// Push a *single‑item* “move” (delete + insert) operation into the batch. + #[allow(clippy::too_many_arguments)] + pub fn batch_move>( + &self, + from_path: SubtreePath<'_, B>, + key: &[u8], + to_path: Vec>, + apply_type: BatchMoveApplyType, + alter_flags_to_new_flags: Option>, + transaction: TransactionArg, + drive_operations: &mut Vec, + drive_version: &DriveVersion, + ) -> Result<(), Error> { + match drive_version.grove_methods.batch.batch_move { + 0 => self.batch_move_v0( + from_path, + key, + to_path, + apply_type, + alter_flags_to_new_flags, + transaction, + drive_operations, + drive_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "batch_move".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs new file mode 100644 index 00000000000..4afd66cba97 --- /dev/null +++ b/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs @@ -0,0 +1,334 @@ +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::fees::op::LowLevelDriveOperation::GroveOperation; +use crate::util::grove_operations::{push_drive_operation_result, BatchMoveApplyType, QueryType}; +use grovedb::batch::key_info::KeyInfo; +use grovedb::batch::{KeyInfoPath, QualifiedGroveDbOp}; +use grovedb::operations::delete::DeleteOptions; +use grovedb::{GroveDb, TransactionArg}; +use grovedb_epoch_based_storage_flags::StorageFlags; +use grovedb_path::SubtreePath; +use grovedb_storage::rocksdb_storage::RocksDbStorage; +use platform_version::version::drive_versions::DriveVersion; + +impl Drive { + #[allow(clippy::too_many_arguments)] + pub(super) fn batch_move_v0>( + &self, + from_path: SubtreePath<'_, B>, + key: &[u8], + to_path: Vec>, + apply_type: BatchMoveApplyType, + alter_flags_to_new_flags: Option>, + transaction: TransactionArg, + drive_operations: &mut Vec, + drive_version: &DriveVersion, + ) -> Result<(), Error> { + // ── 1. Fetch and validate the element ─────────────────────────────── + let mut element = self + .grove_get( + from_path.clone(), + key, + QueryType::StatefulQuery, + transaction, + drive_operations, + drive_version, + )? + .ok_or_else(|| { + Error::Drive(DriveError::ElementNotFound( + "element to move not found".into(), + )) + })?; + + if element.is_any_tree() { + return Err(Error::Drive(DriveError::NotSupported( + "batch_move does not support moving trees", + ))); + } + + // ── 2. Build the delete op ────────────────────────────────────────── + let current_batch = LowLevelDriveOperation::grovedb_operations_batch(drive_operations); + let delete_opts = DeleteOptions { + allow_deleting_non_empty_trees: false, + deleting_non_empty_trees_returns_error: true, + base_root_storage_is_free: true, + validate_tree_at_path_exists: false, + }; + + let delete_op = match apply_type { + BatchMoveApplyType::StatelessBatchMove { + in_tree_type, + estimated_key_size, + estimated_value_size, + .. + } => GroveDb::average_case_delete_operation_for_delete::( + &KeyInfoPath::from_known_owned_path(from_path.to_vec()), + &KeyInfo::KnownKey(key.to_vec()), + in_tree_type, + false, + true, + 0, + (estimated_key_size, estimated_value_size), + &drive_version.grove_version, + ) + .map(|r| r.map(Some)), + BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum, + } => self.grove.delete_operation_for_delete_internal( + from_path, + key, + &delete_opts, + is_known_to_be_subtree_with_sum, + ¤t_batch.operations, + transaction, + &drive_version.grove_version, + ), + }; + + // ── 3. Push delete + insert into the batch ────────────────────────── + if let Some(delete_op) = push_drive_operation_result(delete_op, drive_operations)? { + if let Some(flags) = alter_flags_to_new_flags.as_ref() { + element.set_flags(StorageFlags::map_to_some_element_flags(flags.as_ref())); + } + + drive_operations.push(GroveOperation(delete_op)); + drive_operations.push(GroveOperation(QualifiedGroveDbOp::insert_or_replace_op( + to_path, + key.to_vec(), + element, + ))); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + error::{drive::DriveError, Error}, + util::{ + grove_operations::{BatchMoveApplyType, QueryType}, + test_helpers::setup::setup_drive, + }, + }; + use assert_matches::assert_matches; + use grovedb::{Element, MaybeTree, TreeType}; + use grovedb_path::SubtreePath; + use platform_version::version::PlatformVersion; + + /// Successfully move a single non‑tree item from `root` to `new_root`. + #[test] + fn test_batch_move_single_item_success() { + let drive = setup_drive(None, None); + let platform_version = PlatformVersion::latest(); + let tx = drive.grove.start_transaction(); + + // prepare trees + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"new_root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + + // insert the element to move + let key = b"key1".to_vec(); + let value = Element::new_item(b"value1".to_vec()); + drive + .grove + .insert( + &[b"root".as_slice()], + &key, + value.clone(), + None, + Some(&tx), + &platform_version.drive.grove_version, + ) + .unwrap() + .unwrap(); + + // batch‑move setup + let apply_type = BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum: Some(MaybeTree::NotTree), + }; + let mut ops = Vec::new(); + + // call batch_move + drive + .batch_move_v0( + [b"root".as_slice()].as_slice().into(), + &key, + vec![b"new_root".to_vec()], + apply_type, + None, + Some(&tx), + &mut ops, + &platform_version.drive, + ) + .expect("move should succeed"); + + // apply & commit + drive + .apply_batch_low_level_drive_operations( + None, + Some(&tx), + ops, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + drive.grove.commit_transaction(tx).unwrap().unwrap(); + + // verify new location + let new_val = drive + .grove_get( + [b"new_root".as_slice()].as_slice().into(), + &key, + QueryType::StatefulQuery, + None, + &mut vec![], + &platform_version.drive, + ) + .expect("query") + .expect("value"); + assert_eq!(new_val, value); + + // verify old location empty + let old_res = drive.grove_get( + [b"root".as_slice()].as_slice().into(), + &key, + QueryType::StatefulQuery, + None, + &mut vec![], + &platform_version.drive, + ); + assert_matches!( + old_res, + Err(Error::GroveDB(grovedb::Error::PathKeyNotFound(_))) + ); + } + + /// Attempting to move a missing key should return a PathKeyNotFound error. + #[test] + fn test_batch_move_single_item_missing() { + let drive = setup_drive(None, None); + let platform_version = PlatformVersion::latest(); + let tx = drive.grove.start_transaction(); + + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"new_root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + + let key = b"absent".to_vec(); + let apply_type = BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum: Some(MaybeTree::NotTree), + }; + let mut ops = Vec::new(); + + let res = drive.batch_move_v0( + [b"root".as_slice()].as_slice().into(), + &key, + vec![b"new_root".to_vec()], + apply_type, + None, + Some(&tx), + &mut ops, + &platform_version.drive, + ); + + assert_matches!(res, Err(Error::Drive(DriveError::ElementNotFound(_)))); + } + + /// Moving a subtree (tree element) must fail with NotSupported. + #[test] + fn test_batch_move_single_item_tree_error() { + let drive = setup_drive(None, None); + let platform_version = PlatformVersion::latest(); + let tx = drive.grove.start_transaction(); + + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + drive + .grove_insert_empty_tree( + SubtreePath::empty(), + b"new_root", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + + // insert a subtree under key "sub" + drive + .grove_insert_empty_tree( + [b"root".as_slice()].as_slice().into(), + b"sub", + Some(&tx), + None, + &mut vec![], + &platform_version.drive, + ) + .unwrap(); + + let apply_type = BatchMoveApplyType::StatefulBatchMove { + is_known_to_be_subtree_with_sum: Some(MaybeTree::Tree(TreeType::NormalTree)), + }; + let mut ops = Vec::new(); + + let res = drive.batch_move_v0( + [b"root".as_slice()].as_slice().into(), + b"sub", + vec![b"new_root".to_vec()], + apply_type, + None, + Some(&tx), + &mut ops, + &platform_version.drive, + ); + + assert_matches!(res, Err(Error::Drive(DriveError::NotSupported(_)))); + } +} diff --git a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs index 502943431b1..1d5ca357d4a 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_move_items_in_path_query/v0/mod.rs @@ -125,7 +125,9 @@ impl Drive { push_drive_operation_result(delete_operation, drive_operations)? { if let Some(altered_flags) = alter_flags_to_new_flags.as_ref() { - element.set_flags(StorageFlags::map_to_some_element_flags(altered_flags.as_ref())) + element.set_flags(StorageFlags::map_to_some_element_flags( + altered_flags.as_ref(), + )) } // Add the delete operation to the batch of drive operations drive_operations.push(GroveOperation(delete_operation)); diff --git a/packages/rs-drive/src/util/grove_operations/mod.rs b/packages/rs-drive/src/util/grove_operations/mod.rs index caf8d351a60..b9fc2d28d99 100644 --- a/packages/rs-drive/src/util/grove_operations/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/mod.rs @@ -137,6 +137,7 @@ pub mod batch_insert_sum_item_if_not_exists; /// Moved items that are found in a path query to a new path. pub mod batch_move_items_in_path_query; +mod batch_move; /// Get the total value from a big sum tree pub mod grove_get_big_sum_tree_total_value; /// Get total value from sum tree in grove if it exists diff --git a/packages/rs-drive/src/verify/group/mod.rs b/packages/rs-drive/src/verify/group/mod.rs index 17438f8f624..455847aa63f 100644 --- a/packages/rs-drive/src/verify/group/mod.rs +++ b/packages/rs-drive/src/verify/group/mod.rs @@ -1,5 +1,5 @@ mod verify_action_signers; +mod verify_action_signers_total_power; mod verify_active_action_infos; mod verify_group_info; mod verify_group_infos_in_contract; -mod verify_action_signers_total_power; diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs index 09333eb59f5..215c13f3593 100644 --- a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs @@ -1,7 +1,7 @@ mod v0; use crate::drive::Drive; -use dpp::data_contract::group::{GroupMemberPower, GroupSumPower}; +use dpp::data_contract::group::GroupSumPower; use dpp::data_contract::GroupContractPosition; use dpp::group::group_action_status::GroupActionStatus; use dpp::identifier::Identifier; diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs index 58089623dfe..6e99f3e2482 100644 --- a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs @@ -1,12 +1,12 @@ use crate::drive::Drive; -use grovedb::Element::{SumItem, SumTree}; +use grovedb::Element::SumItem; use crate::error::proof::ProofError; use crate::error::Error; use crate::verify::RootHash; -use dpp::data_contract::group::{GroupMemberPower, GroupSumPower}; +use dpp::data_contract::group::GroupSumPower; use dpp::data_contract::GroupContractPosition; use dpp::group::group_action_status::GroupActionStatus; use dpp::identifier::Identifier; @@ -14,6 +14,7 @@ use grovedb::{GroveDb, TreeFeatureType}; use platform_version::version::PlatformVersion; impl Drive { + #[allow(clippy::too_many_arguments)] pub(super) fn verify_action_signers_total_power_v0( proof: &[u8], contract_id: Identifier, @@ -29,13 +30,21 @@ impl Drive { group_contract_position, action_id.to_buffer(), action_status, - action_signer_id.to_buffer() + action_signer_id.to_buffer(), ); let (root_hash, tree_feature, mut proved_key_values) = if verify_subset_of_proof { - GroveDb::verify_subset_query_get_parent_tree_info(proof, &path_query, &platform_version.drive.grove_version)? + GroveDb::verify_subset_query_get_parent_tree_info( + proof, + &path_query, + &platform_version.drive.grove_version, + )? } else { - GroveDb::verify_query_get_parent_tree_info(proof, &path_query, &platform_version.drive.grove_version)? + GroveDb::verify_query_get_parent_tree_info( + proof, + &path_query, + &platform_version.drive.grove_version, + )? }; if proved_key_values.len() != 1 { diff --git a/packages/rs-drive/src/verify/state_transition/mod.rs b/packages/rs-drive/src/verify/state_transition/mod.rs index 0d27f86f729..5bf508ca3dd 100644 --- a/packages/rs-drive/src/verify/state_transition/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/mod.rs @@ -2,4 +2,4 @@ pub mod verify_state_transition_was_executed_with_proof; /// Defines path queries needed to verify state transitions based on their type and expected result. -pub mod state_transition_execution_path_queries; \ No newline at end of file +pub mod state_transition_execution_path_queries; diff --git a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs index 502b48467cc..6255a829e87 100644 --- a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/mod.rs @@ -1,6 +1,6 @@ -use grovedb::PathQuery; use dpp::identifier::Identifier; use dpp::prelude::DataContract; +use grovedb::PathQuery; use platform_version::version::PlatformVersion; /// Token transition module @@ -45,4 +45,4 @@ pub trait TryTransitionIntoPathQuery { owner_id: Identifier, platform_version: &PlatformVersion, ) -> Result; -} \ No newline at end of file +} diff --git a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs index 6f576be5f3d..440fc21768e 100644 --- a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs +++ b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs @@ -1,4 +1,7 @@ -use grovedb::PathQuery; +use crate::drive::Drive; +use crate::error::Error; +use crate::query::{SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus}; +use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; @@ -7,16 +10,15 @@ use dpp::data_contracts::SystemDataContract; use dpp::group::GroupStateTransitionInfo; use dpp::identifier::Identifier; use dpp::prelude::DataContract; -use dpp::state_transition::batch_transition::batched_transition::token_transition::{TokenTransition, TokenTransitionV0Methods}; +use dpp::state_transition::batch_transition::batched_transition::token_transition::{ + TokenTransition, TokenTransitionV0Methods, +}; use dpp::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use dpp::state_transition::batch_transition::token_mint_transition::v0::v0_methods::TokenMintTransitionV0Methods; use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_methods::TokenTransferTransitionV0Methods; use dpp::system_data_contracts::load_system_data_contract; +use grovedb::PathQuery; use platform_version::version::PlatformVersion; -use crate::drive::Drive; -use crate::error::Error; -use crate::query::{SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus}; -use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; fn create_token_historical_document_query( token_transition: &TokenTransition, @@ -41,37 +43,44 @@ fn create_token_historical_document_query( } fn create_token_group_action_query( - contract_id: [u8;32], - identity_id: [u8;32], - group_state_transition_info: GroupStateTransitionInfo + contract_id: [u8; 32], + identity_id: [u8; 32], + group_state_transition_info: GroupStateTransitionInfo, ) -> PathQuery { let GroupStateTransitionInfo { - group_contract_position, action_id, .. - } =group_state_transition_info; + group_contract_position, + action_id, + .. + } = group_state_transition_info; - Drive::group_active_and_closed_action_single_signer_query(contract_id, group_contract_position, action_id.to_buffer(), identity_id) + Drive::group_active_and_closed_action_single_signer_query( + contract_id, + group_contract_position, + action_id.to_buffer(), + identity_id, + ) } impl TryTransitionIntoPathQuery for TokenTransition { type Error = Error; - fn try_transition_into_path_query_with_contract(&self, contract: &DataContract, owner_id: Identifier, platform_version: &PlatformVersion) -> Result { + fn try_transition_into_path_query_with_contract( + &self, + contract: &DataContract, + owner_id: Identifier, + platform_version: &PlatformVersion, + ) -> Result { let token_id = self.token_id(); - let token_config = contract.expected_token_configuration( - self.base().token_contract_position(), - )?; + let token_config = + contract.expected_token_configuration(self.base().token_contract_position())?; let keeps_historical_document = token_config.keeps_history(); let action_success_query = match self { TokenTransition::Burn(_) => { if keeps_historical_document.keeps_burning_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_balance_for_identity_id_query( token_id.to_buffer(), @@ -81,14 +90,9 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::Mint(token_mint_transition) => { if keeps_historical_document.keeps_minting_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { - let recipient_id = - token_mint_transition.recipient_id(token_config)?; + let recipient_id = token_mint_transition.recipient_id(token_config)?; Drive::token_balance_for_identity_id_query( token_id.into_buffer(), @@ -98,15 +102,10 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::Transfer(token_transfer_transition) => { if keeps_historical_document.keeps_transfer_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { let recipient_id = token_transfer_transition.recipient_id(); - let identity_ids = - [owner_id.to_buffer(), recipient_id.to_buffer()]; + let identity_ids = [owner_id.to_buffer(), recipient_id.to_buffer()]; Drive::token_balances_for_identity_ids_query( token_id.into_buffer(), @@ -116,11 +115,7 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::Freeze(_) => { if keeps_historical_document.keeps_freezing_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_info_for_identity_id_query( token_id.to_buffer(), @@ -130,11 +125,7 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::Unfreeze(_) => { if keeps_historical_document.keeps_freezing_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_info_for_identity_id_query( token_id.to_buffer(), @@ -144,11 +135,7 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::DirectPurchase(_) => { if keeps_historical_document.keeps_direct_purchase_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_balance_for_identity_id_query( token_id.to_buffer(), @@ -158,11 +145,7 @@ impl TryTransitionIntoPathQuery for TokenTransition { } TokenTransition::SetPriceForDirectPurchase(_) => { if keeps_historical_document.keeps_direct_pricing_history() { - create_token_historical_document_query( - self, - owner_id, - platform_version, - )? + create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_direct_purchase_price_query(token_id.to_buffer()) } @@ -170,26 +153,31 @@ impl TryTransitionIntoPathQuery for TokenTransition { TokenTransition::DestroyFrozenFunds(_) | TokenTransition::EmergencyAction(_) | TokenTransition::ConfigUpdate(_) - | TokenTransition::Claim(_) => create_token_historical_document_query( - self, - owner_id, - platform_version, - )?, + | TokenTransition::Claim(_) => { + create_token_historical_document_query(self, owner_id, platform_version)? + } }; if let Some(group_state_transition_info) = self.base().using_group_info() { // if we are using group info, one of two things might have happened // either the action happened or it was initiated // we need to merge the action success query and the query for group signers - let group_signer_query = create_token_group_action_query(self.data_contract_id().to_buffer(), owner_id.to_buffer(), group_state_transition_info); + let group_signer_query = create_token_group_action_query( + self.data_contract_id().to_buffer(), + owner_id.to_buffer(), + group_state_transition_info, + ); let mut success_query = action_success_query; // We need to remove the limit here success_query.query.limit = None; - PathQuery::merge(vec![&group_signer_query, &success_query], &platform_version.drive.grove_version).map_err(Error::GroveDB) - + PathQuery::merge( + vec![&group_signer_query, &success_query], + &platform_version.drive.grove_version, + ) + .map_err(Error::GroveDB) } else { // if there is no group info that all we need is this query Ok(action_success_query) } } -} \ No newline at end of file +} diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index 6779628bea8..c645f51c494 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -55,7 +55,6 @@ use crate::verify::RootHash; use crate::error::Error; use crate::error::proof::ProofError; use crate::query::{ContractLookupFn, SingleDocumentDriveQuery, SingleDocumentDriveQueryContestedStatus}; -use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; impl Drive { #[inline(always)] @@ -339,7 +338,8 @@ impl Drive { SingleDocumentDriveQueryContestedStatus::NotContested, }; - let is_group_action = token_transition.base().using_group_info().is_some(); + let is_group_action = + token_transition.base().using_group_info().is_some(); let (root_hash, document) = query.verify_proof( is_group_action, // it will be a subset if it is a group action @@ -347,16 +347,17 @@ impl Drive { token_history_document_type, platform_version, )?; - + if let Some(document) = &document { - let expected_document = token_transition.build_historical_document( - token_id, - owner_id, - identity_contract_nonce, - &BlockInfo::default(), - token_config, - platform_version, - )?; + let expected_document = token_transition + .build_historical_document( + token_id, + owner_id, + identity_contract_nonce, + &BlockInfo::default(), + token_config, + platform_version, + )?; // Some fields are populated by the drive, // so we need to ignore them @@ -376,15 +377,30 @@ impl Drive { return Err(Error::Proof(ProofError::UnexpectedResultProof(format!("proof of state transition execution did not show the correct historical document got: [{}] vs expected: [{}], state transition is [{}]", document, expected_document, token_transition)))); } } - - if let Some(group_state_transition_info) = token_transition.base().using_group_info() { + + if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { let action_status = if document.is_some() { GroupActionStatus::ActionClosed } else { GroupActionStatus::ActionActive }; - let sum_power = Drive::verify_action_signer_and_total_power(proof, data_contract_id, group_state_transition_info.group_contract_position, action_status, group_state_transition_info.action_id, owner_id, true, platform_version)?.1; - Ok((root_hash, VerifiedTokenGroupActionWithDocument(sum_power, document))) + let sum_power = Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + action_status, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )? + .1; + Ok(( + root_hash, + VerifiedTokenGroupActionWithDocument(sum_power, document), + )) } else { let document = document.ok_or(Error::Proof(ProofError::IncorrectProof(format!("proof did not contain document of type `{}` expected to exist because the token keeps historical documents", token_transition.historical_document_type_name()))))?; Ok((root_hash, VerifiedTokenActionWithDocument(document))) diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/mod.rs index 71d7fc8b00c..632a7b59291 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/mod.rs @@ -57,6 +57,7 @@ pub struct DriveGroveBatchMethodVersions { pub batch_delete_up_tree_while_empty: FeatureVersion, pub batch_refresh_reference: FeatureVersion, pub batch_insert_empty_sum_tree: FeatureVersion, + pub batch_move: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/v1.rs index 0fa2d7d09c9..b9cfbbc399f 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_grove_method_versions/v1.rs @@ -48,6 +48,7 @@ pub const DRIVE_GROVE_METHOD_VERSIONS_V1: DriveGroveMethodVersions = DriveGroveM batch_delete_up_tree_while_empty: 0, batch_refresh_reference: 0, batch_insert_empty_sum_tree: 0, + batch_move: 0, }, apply: DriveGroveApplyMethodVersions { grove_apply_operation: 0, diff --git a/packages/rs-sdk/tests/fetch/group_actions.rs b/packages/rs-sdk/tests/fetch/group_actions.rs index 41e332d135f..2a030dc34d8 100644 --- a/packages/rs-sdk/tests/fetch/group_actions.rs +++ b/packages/rs-sdk/tests/fetch/group_actions.rs @@ -150,7 +150,7 @@ async fn test_fetch_all_group_actions() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), }))) if note == "world on fire" ); } @@ -182,7 +182,7 @@ async fn test_fetch_one_group_action_since_existing_one_with_limit() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), }))) if note == "world on fire" ); } From 22a6dff013cb148b194c85dc7e2d6aa291cdaaf0 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 12 May 2025 22:13:15 +0700 Subject: [PATCH 3/9] more proof fixes --- Cargo.lock | 18 ++--- .../rs-dpp/src/group/group_action_status.rs | 2 +- .../src/state_transition/proof_result.rs | 2 + .../batch/tests/token/mint/mod.rs | 8 +- packages/rs-drive/Cargo.toml | 12 +-- packages/rs-drive/src/drive/group/queries.rs | 22 +++++ .../verify_action_signers_total_power/mod.rs | 5 +- .../v0/mod.rs | 63 +++++++++++++-- .../v0/mod.rs | 80 ++++++++++++++++++- packages/rs-platform-version/Cargo.toml | 2 +- 10 files changed, 183 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc73ab04a69..cea6f3bc3dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2169,7 +2169,7 @@ dependencies = [ [[package]] name = "grovedb" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "axum 0.7.5", "bincode", @@ -2201,7 +2201,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "integer-encoding", "intmap", @@ -2211,7 +2211,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "grovedb-costs", "hex", @@ -2223,7 +2223,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "bincode", "bincode_derive", @@ -2247,7 +2247,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "hex", ] @@ -2255,7 +2255,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "blake3", "grovedb-costs", @@ -2274,7 +2274,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "thiserror 2.0.12", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2283,7 +2283,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "hex", "itertools 0.14.0", @@ -2292,7 +2292,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=b9c8d7faa2e3026cd0110acedc1e63b00b273a3d#b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" +source = "git+https://github.com/dashpay/grovedb?rev=221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8#221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" dependencies = [ "serde", "serde_with 3.9.0", diff --git a/packages/rs-dpp/src/group/group_action_status.rs b/packages/rs-dpp/src/group/group_action_status.rs index 95a7e54dc11..407d2a7c860 100644 --- a/packages/rs-dpp/src/group/group_action_status.rs +++ b/packages/rs-dpp/src/group/group_action_status.rs @@ -1,6 +1,6 @@ use anyhow::bail; -#[derive(Debug, PartialEq, PartialOrd, Clone, Eq)] +#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Eq)] pub enum GroupActionStatus { ActionActive, ActionClosed, diff --git a/packages/rs-dpp/src/state_transition/proof_result.rs b/packages/rs-dpp/src/state_transition/proof_result.rs index 7be9a4a6dd7..248629015d2 100644 --- a/packages/rs-dpp/src/state_transition/proof_result.rs +++ b/packages/rs-dpp/src/state_transition/proof_result.rs @@ -2,6 +2,7 @@ use crate::balances::credits::TokenAmount; use crate::data_contract::group::GroupSumPower; use crate::data_contract::DataContract; use crate::document::Document; +use crate::group::group_action_status::GroupActionStatus; use crate::identity::{Identity, PartialIdentity}; use crate::tokens::info::IdentityTokenInfo; use crate::tokens::status::TokenStatus; @@ -25,6 +26,7 @@ pub enum StateTransitionProofResult { VerifiedDocuments(BTreeMap>), VerifiedTokenActionWithDocument(Document), VerifiedTokenGroupActionWithDocument(GroupSumPower, Option), + VerifiedTokenGroupActionWithTokenBalance(GroupSumPower, GroupActionStatus, Option), VerifiedMasternodeVote(Vote), VerifiedNextDistribution(Vote), } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index 81e05d68668..a0d83b252e5 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -1099,6 +1099,7 @@ mod token_mint_tests { use dpp::data_contract::change_control_rules::ChangeControlRules; use dpp::data_contract::group::v0::GroupV0; use dpp::data_contract::group::Group; + use dpp::group::group_action_status::GroupActionStatus; use dpp::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; use dpp::state_transition::batch_transition::TokenMintTransition; use dpp::state_transition::proof_result::StateTransitionProofResult; @@ -1562,9 +1563,10 @@ mod token_mint_tests { } else { assert_matches!( result, - StateTransitionProofResult::VerifiedTokenBalance(id, amount) => { - assert_eq!(id, identity.id()); - assert_eq!(amount, 1337); + StateTransitionProofResult::VerifiedTokenGroupActionWithTokenBalance(power, status, balance) => { + assert_eq!(power, 1); + assert_eq!(status, GroupActionStatus::ActionActive); + assert_eq!(balance, Some(100000)); } ); } diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 94437c7c594..4f94d1bc80d 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/group/queries.rs b/packages/rs-drive/src/drive/group/queries.rs index 2630d2f6e91..aba05b89a3d 100644 --- a/packages/rs-drive/src/drive/group/queries.rs +++ b/packages/rs-drive/src/drive/group/queries.rs @@ -223,6 +223,28 @@ impl Drive { } } + /// Gets query to figure out if the proof is for active or closed + pub fn group_active_or_closed_action_query( + contract_id: [u8; 32], + group_contract_position: GroupContractPosition, + ) -> PathQuery { + let group_path = group_path_vec(&contract_id, group_contract_position); + let mut query = Query::new_with_direction(true); + query.insert_keys(vec![ + GROUP_ACTIVE_ACTIONS_KEY.to_vec(), + GROUP_CLOSED_ACTIONS_KEY.to_vec(), + ]); + + PathQuery { + path: group_path, + query: SizedQuery { + query, + limit: None, + offset: None, + }, + } + } + /// Gets the signer in both the active and closed action places pub fn group_active_and_closed_action_single_signer_query( contract_id: [u8; 32], diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs index 215c13f3593..629636be590 100644 --- a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/mod.rs @@ -45,16 +45,17 @@ impl Drive { /// # Versioning /// - Dispatches to `verify_action_signers_total_power_v0` when `platform_version.drive.methods.verify.group.verify_action_signers_total_power == 0`. /// + #[allow(clippy::too_many_arguments)] pub fn verify_action_signer_and_total_power( proof: &[u8], contract_id: Identifier, group_contract_position: GroupContractPosition, - action_status: GroupActionStatus, + action_status: Option, action_id: Identifier, action_signer_id: Identifier, verify_subset_of_proof: bool, platform_version: &PlatformVersion, - ) -> Result<(RootHash, GroupSumPower), Error> { + ) -> Result<(RootHash, GroupActionStatus, GroupSumPower), Error> { match platform_version .drive .methods diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs index 6e99f3e2482..4e30568f913 100644 --- a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs @@ -10,7 +10,7 @@ use dpp::data_contract::group::GroupSumPower; use dpp::data_contract::GroupContractPosition; use dpp::group::group_action_status::GroupActionStatus; use dpp::identifier::Identifier; -use grovedb::{GroveDb, TreeFeatureType}; +use grovedb::{Element, GroveDb, TreeFeatureType, VerifyOptions}; use platform_version::version::PlatformVersion; impl Drive { @@ -19,12 +19,63 @@ impl Drive { proof: &[u8], contract_id: Identifier, group_contract_position: GroupContractPosition, - action_status: GroupActionStatus, + action_status: Option, action_id: Identifier, action_signer_id: Identifier, verify_subset_of_proof: bool, platform_version: &PlatformVersion, - ) -> Result<(RootHash, GroupSumPower), Error> { + ) -> Result<(RootHash, GroupActionStatus, GroupSumPower), Error> { + let action_status = match action_status { + Some(action_status) => action_status, + None => { + // We don't actually know the action status, we need to look it up from the proof + let path_query = Drive::group_active_or_closed_action_query( + contract_id.to_buffer(), + group_contract_position, + ); + let mut proved_key_values = GroveDb::verify_query_with_options( + proof, + &path_query, + VerifyOptions { + absence_proofs_for_non_existing_searched_keys: false, + verify_proof_succinctness: false, + include_empty_trees_in_result: true, + }, + &platform_version.drive.grove_version, + )? + .1; + + if proved_key_values.len() != 2 { + return Err(Error::Proof(ProofError::CorruptedProof(format!( + "we should always get back group action statuses for open and closed, we got {}", + proved_key_values.len() + )))); + } + + let Some(Element::Tree(active_root, _)) = proved_key_values.remove(0).2 else { + return Err(Error::Proof(ProofError::CorruptedProof( + "group active action should be returned".to_string(), + ))); + }; + let Some(Element::Tree(closed_root, _)) = proved_key_values.remove(0).2 else { + return Err(Error::Proof(ProofError::CorruptedProof( + "group closed action should be returned".to_string(), + ))); + }; + if active_root.is_some() && closed_root.is_some() + || active_root.is_none() && closed_root.is_none() + { + return Err(Error::Proof(ProofError::CorruptedProof( + "group action should be either active or closed".to_string(), + ))); + } + if active_root.is_some() { + GroupActionStatus::ActionActive + } else { + GroupActionStatus::ActionClosed + } + } + }; let path_query = Drive::group_active_or_closed_action_single_signer_query( contract_id.to_buffer(), group_contract_position, @@ -54,11 +105,13 @@ impl Drive { )))); } - let element = proved_key_values.remove(0).2; + let path_key_optional_element_trio = proved_key_values.remove(0); + + let element = path_key_optional_element_trio.2; match element { Some(SumItem(..)) => { if let TreeFeatureType::SummedMerkNode(aggregate_power) = tree_feature { - Ok((root_hash, aggregate_power as GroupSumPower)) + Ok((root_hash, action_status, aggregate_power as GroupSumPower)) } else { Err(Error::Proof(ProofError::IncorrectProof( "we expected a summed tree".to_string(), diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index c645f51c494..c721d16c2da 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -41,7 +41,7 @@ use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_m use dpp::state_transition::batch_transition::token_unfreeze_transition::v0::v0_methods::TokenUnfreezeTransitionV0Methods; use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; use dpp::state_transition::proof_result::StateTransitionProofResult; -use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenGroupActionWithDocument, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; +use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenGroupActionWithDocument, VerifiedTokenGroupActionWithTokenBalance, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; use dpp::system_data_contracts::{load_system_data_contract, SystemDataContract}; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; use dpp::voting::vote_polls::VotePoll; @@ -390,13 +390,13 @@ impl Drive { proof, data_contract_id, group_state_transition_info.group_contract_position, - action_status, + Some(action_status), group_state_transition_info.action_id, owner_id, true, platform_version, )? - .1; + .2; Ok(( root_hash, VerifiedTokenGroupActionWithDocument(sum_power, document), @@ -410,6 +410,42 @@ impl Drive { TokenTransition::Burn(_) => { if keeps_historical_document.keeps_burning_history() { historical_query() + } else if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { + let (_root_hash, status, sum_power) = + Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + None, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )?; + + let (root_hash, balance) = + Drive::verify_token_balance_for_identity_id( + proof, + token_id.into_buffer(), + owner_id.into_buffer(), + true, + platform_version, + )?; + if status == GroupActionStatus::ActionClosed + && balance.is_none() + { + return Err(Error::Proof(ProofError::IncorrectProof( + format!("proof did not contain token balance for identity {} expected to exist because of state transition (token burn)", owner_id)))); + }; + + Ok(( + root_hash, + VerifiedTokenGroupActionWithTokenBalance( + sum_power, status, balance, + ), + )) } else { let (root_hash, Some(balance)) = Drive::verify_token_balance_for_identity_id( @@ -421,7 +457,7 @@ impl Drive { )? else { return Err(Error::Proof(ProofError::IncorrectProof( - format!("proof did not contain token balance for identity {} expected to exist because of state transition (token burn)", owner_id)))); + format!("proof did not contain token balance for identity {} expected to exist because of state transition (token burn)", owner_id)))); }; Ok((root_hash, VerifiedTokenBalance(owner_id, balance))) } @@ -429,6 +465,42 @@ impl Drive { TokenTransition::Mint(token_mint_transition) => { if keeps_historical_document.keeps_minting_history() { historical_query() + } else if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { + let (_root_hash, status, sum_power) = + Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + None, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )?; + + let (root_hash, balance) = + Drive::verify_token_balance_for_identity_id( + proof, + token_id.into_buffer(), + owner_id.into_buffer(), + true, + platform_version, + )?; + if status == GroupActionStatus::ActionClosed + && balance.is_none() + { + return Err(Error::Proof(ProofError::IncorrectProof( + format!("proof did not contain token balance for identity {} expected to exist because of state transition (token mint)", owner_id)))); + }; + + Ok(( + root_hash, + VerifiedTokenGroupActionWithTokenBalance( + sum_power, status, balance, + ), + )) } else { let recipient_id = token_mint_transition.recipient_id(token_config)?; diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index fcfaa68527e..46b4a7149cc 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.0-rc.3" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "b9c8d7faa2e3026cd0110acedc1e63b00b273a3d" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "221ca6ec6b8d2b192d334192bd5a7b2cab5b0aa8" } once_cell = "1.19.0" [features] From f4a56243d47cba77bb61ac224a384d45ad11c3da Mon Sep 17 00:00:00 2001 From: lklimek <842586+lklimek@users.noreply.github.com> Date: Mon, 12 May 2025 18:11:29 +0200 Subject: [PATCH 4/9] test(sdk): fix sdk group_actions tests and regenerate test vectors (#2604) --- packages/rs-drive/src/lib.rs | 2 +- packages/rs-drive/src/prove/mod.rs | 1 + packages/rs-sdk/tests/fetch/group_actions.rs | 4 ++-- ...2faa91c6f6f6d2f379d9e2937e515e3bead2a.json | Bin 51919 -> 53386 bytes ...99a143d6ae990bb9bc89cb34f7c5db8b1b705.json | 1 - ...4670383282934865ccc3241c0f7fedf218853.json | 1 + ...d3782117d6bc7d324bde7f4a9de01bf66777c.json | Bin 52449 -> 53880 bytes ...30a1c696ab53d03e0b241f35f9d002eee3233.json | 1 + ...99a143d6ae990bb9bc89cb34f7c5db8b1b705.json | 1 - 9 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json create mode 100644 packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json create mode 100644 packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json delete mode 100644 packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json diff --git a/packages/rs-drive/src/lib.rs b/packages/rs-drive/src/lib.rs index a13845e0fcd..8337e87fda0 100644 --- a/packages/rs-drive/src/lib.rs +++ b/packages/rs-drive/src/lib.rs @@ -55,5 +55,5 @@ pub mod state_transition_action; #[cfg(any(feature = "server", feature = "verify", feature = "fixtures-and-mocks"))] pub mod util; /// Contains a set of useful grovedb proof verification functions -#[cfg(feature = "verify")] +#[cfg(any(feature = "server", feature = "verify"))] pub mod verify; diff --git a/packages/rs-drive/src/prove/mod.rs b/packages/rs-drive/src/prove/mod.rs index ad5842ffaf2..62ebbe02cfc 100644 --- a/packages/rs-drive/src/prove/mod.rs +++ b/packages/rs-drive/src/prove/mod.rs @@ -1,3 +1,4 @@ mod prove_elements; mod prove_multiple_state_transition_results; +#[cfg(feature = "server")] mod prove_state_transition; diff --git a/packages/rs-sdk/tests/fetch/group_actions.rs b/packages/rs-sdk/tests/fetch/group_actions.rs index 2a030dc34d8..98282439406 100644 --- a/packages/rs-sdk/tests/fetch/group_actions.rs +++ b/packages/rs-sdk/tests/fetch/group_actions.rs @@ -150,7 +150,7 @@ async fn test_fetch_all_group_actions() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - contract_id: DATA_CONTRACT_ID, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), }))) if note == "world on fire" ); } @@ -182,7 +182,7 @@ async fn test_fetch_one_group_action_since_existing_one_with_limit() { assert_matches!( group_actions.get(&GROUP_ACTION_ID), Some(Some(GroupAction::V0(GroupActionV0 { - contract_id: DATA_CONTRACT_ID, token_contract_position: 0, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), + contract_id: DATA_CONTRACT_ID, token_contract_position: 2, event: GroupActionEvent::TokenEvent(TokenEvent::Burn(10, Some(note))), }))) if note == "world on fire" ); } diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/msg_GetGroupActionsRequest_e11730d47bec719aa135407aaf42faa91c6f6f6d2f379d9e2937e515e3bead2a.json index 82ca6d81ac79c4b52b76caad1e8f4d0b67e9d614..cd259253587d3a35ea5c708354ad90f205cae156 100644 GIT binary patch delta 2927 zcmYLLZE#f88J>`vy^aZFsS^S;A(1#!N$%ZycF*pHB45+g03FdS4Ec`P0BRyjN^Px! z#AI=%GZx5)d9)^c7%W1Bgdt&3zz|qO%LpoQ+RmtdOsAurYN26jOFK?OpL1?jW-_yz zRXw1NZcLkaY0+`%saC7cLYw@Zry?lHsWV8 z@b{pNXuSuMmx_C0CbBmOe04-1d!d75wt-t67QQX@LyS3ytTQn#98BgI$U7@AF<`MR zfys9`&It?2$}pmR2CkLb$j=psjs@UbBr(O`BkLrOF%ccI*rEqp3jFBs22j}Q!?pG> zRUpGx?4RO?4t9JZCm3G*b9 z9RlNyktoS|;2y*+5AJ%?B2*sil0HODi4WO(JZQq+dgt#~FLECDEW}$qc&W*cq=LKL zfElwfksHRNPv3#;)47g11h4K{#M&n2B`j=PCE0)nvR-f=KQ_*1-zLug(AKc2vW4Y7 ztUbv&ekFBF?bRiE6AF?DVRSzoe39!%Q=MW0TWldjY$XL-B+tP7XKd`wk%;F?RE~T{ zDmCr(5`n+Wb|3}~?B8x_@)TTfOV^Aj)D3)eQQ#-l?!^1}{r$@_Er^f9oNv8EXEA~8 z2;jtk&0R{prknd3^oUZd+!R2htXOmU=q?AZ zwu_!VTMr)HYbD~#ev#30pN;N5KDyLO+>E?C3*9kSx80r8bE}DeG`Jf5b*@iqe0WI^ zm8S#ft~Bt${VrD-9 z@)XtYihI7>AIF}oe*w4KGZLRXUN}n=Z(^gxaZPv^Xj7ghjY}fslv8yAU($ZMX&1{# z2jwZM^w;M(?h-$aw2@$h=(AM<4X?WkzT08DTI0pK#p_kY$f|YSw}ac6s$h*S)_lcA zJKa~2u4|T?0*bIHI~@-P;N|~5c!G&Q=k7Rdq0Lq!QZNtlT#`2us!-=AI(M9!<$8>z zp*@x<@GsUJcgU8fUHhcK)4~{8@5em4M&XDOw33QI<}r3mIbAOyReOUN9$>c%0_3Ei zgoEskJl!kNCzPYQ{PWQSl`$yJu9(LYyb^z<^SX6xT&;89UBz>FhC=dDc5$^Xkl5Bz z>%l>4{>FnAnWxC8l)B@23&S+U`*Rd91SnVko+vDuOM*Z`R4tZ(ZkM>S)WMnac8@S2DEx^= zsbrvn@5u_fJj2+#%D`{ORC2kNn@IfKJNh(v2uwXP||i`bL`%4{-wT79v9L z*|)}48~%J3+tlH!lEcJTc`RRK17i}$-yGZzM5Fgv;X0{Kj1abv7VZ%?Z$b|K8}q~Wmf&krJFlCYwsy?hGsyuai}H|R ziY=YuP;~mkIS0Lm1Rj20g`+$2dy51|k@$vIIt_tB>yfk6cOT<;+UUneQ_*Z|Ek$2p z#!}zzE7W(fLG%P1mQy#QR359V&PUYk(Hd8$JDw-Qc~wxxK65oApG!*F(7DrL4Av;- zdywHTLCsc%Ft0%*yzZlHli-y=pskU&Jzkx@GpzZ|8aGBIWuESlQASl|(L?yqa2@?jQ(7q`vLk9AWqmZ7y}gbMFv@gyY@H7C6A0 XgCdej8kbPgAm_u=gO%gqD?9%W7%UrC}G>V@`!v^Fd(AwXa;Bta>}K#P=@K;l+Po0kf!Qh(A`(C^$?+kLWk z_s*SjzQ1!ij}_fMT$G(!WF;2cNze9iXwqCfU9{cs?BqiHvG}VPe$2q{i)P~Q&(1@M z5uYOmQ+Cp~6N~I*+)gTaE}1BN8C>v+@sYkz@xpH#({kS#|IcwaxxmNVR)ut_iBR6b zzpwb{FIEVb`gmC8;M}q}YGWpzQ68=~yWO`f96jN{X>$>|=;7L~IEqesJm%}8k%Wmz zRSXxd+8CH<;ENqTIzldneFL-lTnyD2NOvkY11`c97SbDS)V3vZnDw5*vYScN zXLyZ?BU@v*b9OwQdeuRnVDn}S2e+A6HQ7dbov)i)ep4DYacYl;p7X@XM8mhDP)qdD z+^WeB%kkw`*j>az)d?G)H1Hk*@Zm0bj^(`X;N9siI=T|Lvx1;)_OWw?iR1je0-tpkP7%(uuRo-JF-xwtmPN7c+E$^pLTb{!9l`&*G z;~3~Quw$h{#SavQNV>51laRvKLUB!ec)dc+U{LeLavPt8JS<)B!_6vW%p@Z0LDBS| z=hQ+fn54}$V7_gmrQSe(y28yeSHJm%4iB$3CNZ!(hRS9i`Q0RSMgqUss*pVv$Ne9A zG?zBg#0kQIiQcJFyoogZ7vSumc7}l~jo-+rC4aMYshbxme7b>kP6^y3f)iJzRo*JimGz?Yj^i#ST6h)WXR;xMHmq%Ds)B#7V@TqteCCOM+4 z9*VdnZ=kg^N<0=}Fdo(Vcw<@=*^4wMv5Gmt>yAvNu|%-Dg8=Be=@fVw!8VFr)h?=< zbh){Fqfv^PAs>IL@p1ov!fMu;ZMD#`r$P%z_gQ#>5FEPYvR5BVD0iP^2M~@CC1;lI zC8S%~D{(ly$%c7W1joWC;vvEvq=buAeLf=dVhA-kcxF)H3EnLQys&Qx2gv5 z%5Z85kXL>qHyL<*p@WyJ_y(5qtWsFB*2ild@q$7W9?TYk#>0Iph6f!cnwdc(vt)RZ zRN&+F0nRs8b9XOuY6{OV7UayWvXK#LKbGV!Va>zI4{e-(djo#DLt(}>jsiXVgV;|C z=LI3wc6p;oDv|exSstFOQaWFx>F!M;joK`KAeBizL8Q$geoUU6t9GQ*g?J4=``a4TM+AuN%dgmpGG z`C5xDds!bxay##p?@w}%ks4(FG;E9G%M;Wk*_N?dDX~*8Il=b9X~2;tgGDC$$DI!IBZyt*675{wYf6={SC)$7gI!}YWmTQW-N%K^ zZi3km)$9mzPMeSBuyk*r*HifAtvD`}u&k7cr|I+vlc2qd3BatIPL$I7Ol+~{QXO{- zi%%3Y+2PWSV|U28EF)SF{JchS-W+O6j)r;V1>z{18zmko4+cQLE2m^Cjf`?JLMZRz z&@{$Xy&9P=1vrA37&_qSt#l-1CQJ#;Agqb%MwYM(R}~rt6mD(NH5%Nfy-|sWPtC;R HcYgFgrHq4m diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json deleted file mode 100644 index e048700d163..00000000000 --- a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json +++ /dev/null @@ -1 +0,0 @@ -acbfb39d5f22cd2f096af600d2f19d618fa9898fecd778393c777d1b1785ee5fbad857cf62b3b69ade96894bafe42c7c \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json new file mode 100644 index 00000000000..9a3152f6510 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_fetch_all_group_actions/quorum_pubkey-106-4308e13f7335a56c12a2fd4a2e64670383282934865ccc3241c0f7fedf218853.json @@ -0,0 +1 @@ +8ee1e9729e6a059cffb01530a9f823b0ed7fc60e93d3a0bbb99d3940bb62939d0394ec83e0abb9b16e1233907d9e36f7 \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/msg_GetGroupActionsRequest_626170b2b5665f3b4021607da73d3782117d6bc7d324bde7f4a9de01bf66777c.json index e892931b8c76da0d2d48349f19e90c579354fc9e..17fa25a7e038d4e04bf59d9fc4f741200b4f327d 100644 GIT binary patch delta 2953 zcmYjTTWnO<6_uOoneZ?khldRYzle$$-#hory)&K|Q<#t10s;X+c{sM20oBBSi~SMB z#Rk`uN>vMq-B~{^w!w&mg20ZGffC-Pypoa%M^#iMZ5&roK3b%fuLcQ1y7swa(;wgQ zz31$+_u6Z%eg5&g=N`TN+;DleKT>H%UDHMB=2EQ7Zpw17uJQXQdEUTR+4Jyeq7218 zr#KolBQKfJh^&pq$Hq{6`ystv(#vZy{b5naBVweNJkcfsao; z3nyVi=PaEe{OXY4Xj@}ff50RvNd zOyn#HV``g=FC7QL%Mt7>cvhyACnZU_(}!%P9>brb7K$6A`1**&y^81fvHvFb@LNZz zi(@Z180|Mua@NANd8W>5&LH!ta&?@wFV!PZX5jH5=K7`||6Reo0YB#Mv5=T+pnkn`}iymWAH5#r#>^rl0)xZELy*J~`4!wZR!u4}v@Lj2j*ZFT9>%WnBKeOh`5UZH^z6~tc0 z)>KMn5-Q2b#^5`i+QweC8JxXma=mE|FGWD9J>FiLM7EcS{_5QlOZep1>v- zYi?gV%=ZUW>S!3lt#(?iY0V~atn|S8MY-Cs`Y2^_8(|vVCAHnF``Q&9r@{-dBU0zNPR))0# zq|^mrLCB9we+c3UODrOn#R869WWKd*^>1i&NuEpFh_CkJ?EsvR!JlVSLou}Gi7}EEG|3aDMSl?E zl`eX*#Y~3_mDc_fi#U)%y=kGC7`}^(tYKiiALX+Yjt3l6=0$Lpv|dB*|A0CY2Y98> zO?5hWR20rwjD;_@+SuLVF+||>+)>{1_lphbFI%5xBi`#v(XYueNyWJG!ldXej0L0<>&JJuSqJN}=?MetV>sF$g}T8(MT%eBWTjjB z+H-X=xeLp&@Fxn@m0mVoGz{UMG;EAqoe@ylgFoBj;Qsq;6bI;ol_7jG;OfKT^W90H z#yJmU*ai-I&yJn+Y8NSbMm!K=DEbZiap_G9C%V1f+ds23SA=toA@+%demLUd$J=d8 z$qyU6PgEjndKu<@Y@+)8Eok53qJ>jZbYD;7mj;e=Gzn4uzRbd9YWK`L9?O^0e(UR^ zcwEG0G*C;-Ew_*3jOI4uq^GGIPUR0d>0fD(J)G{hbj~Gx(lJGf6l+*jj&YVFvF4$k0Cn~s5lig$yFYm!%)tpDeg z5dJjm5)Bt?S1H~tyqMLlhv4-(g{3VDp`-N~XI8vt*x!qKGp>l>h`FvJO&_*<6RW$J zCVn)8i#c{?l8VuiUG+*P)x=f-5ldN_=97iGl-C>je5xv|r+d%$mN#6kmp-cToqvhnsA4Dp&&d)Lj1Aur(|d54tQ714J< zc@{S+f?t7m<(`>|KS3$!tIW_Poa~S3Q)1U8g?yHg$NYU*g?SgH_bnmidY@5H_K$t! r@k7Rmic&^3+(X(`^?R2~xm1cjiw@@Q(Ro7}+xmfy$J?-1!`=S@5)f-@ delta 3058 zcmYLLZEO_R6_sH;6PXXM4FLjiyiheN?99&FnO)m_C03dSYzVPLpoagDlU%t1Dnq+6aqMw9n$c%m`0T<5fV_VN=Rw#sH&0zg`WFn4Zn8v z=DqvgJ@=e*?>Fxczw?{n$0NDJ5;1Hrzl}w?YjR@vupEB@|+==++@)S%ly`uPZ zJ5gdMd^=udCp_p5?#Dhhi9k67FvVGh!$ETaDuXFI$w-ov_9@ObLDmJ?44_U~J zbMP=v;n}eY2R|{9e_r9-6&J|@18ehKgrAFJL6eKS(-o547ViGsPVv)sjyu`ywqBNF zFV0fvD|FFbYGCA#75>g+FNS&L3JXg=w$Wc|A-p8mU$MhR{!GR27Cz5+@b*~`Ug59AZZ5X<-M^@{F*x4B!$oeYJBBOcTvT1PaB%Ka zG^~i>r;~hi?Q`)GpDLjn4>>4j8r`cMKIL^JlMbp*ns7$C7_2mKq|WBv7~1EUSY0Eb zeePey?>35`VT_QK&Wt-S1`~@3@fj1%-KD_!ynm5fp5XZLVz4e@0?{*sB|787lIL+WB6vx#v@ zjm#7q$LlR7>tWZtDDvApv@JHUd!9n&n+msQ1n1{pb2RqhT9-9fC|_aVZ`&3A`m~!W zh@p0|k9WJtQrp0}r&zv51FN^n(@OAJorC6jg{p17hT{4bfyBg*B2k2iZ@Z+{ZDLxH zgN6OINm3|? z8O1+O1T>vB+176mO6@OjFoKlj{W)=LSQyhpUiz_%YnL1rGLA3D2pN4%yd!lUoEEZ( zNx!yEk<>0& zsFSH|{heH5kqI7p-XpcMQjiOOHi>LtNe0GHIgBf`&qQp^I)VD53PN3Ap~taxi9~(t zHYpsUDjWLZggh{qiX9GmE}B%P4Mn#7HqXZ9H6`fJ<2vE)?H^((K^tM=m!ri60_UiU zDV^}6YB#N3eMOS2&PrZEz3*VuDQc|LOy@7$J35<*(q16Ch};M~Yk!$bUMgxO$f`YL zQ2zn<57Ea~PobBQAPpU*=q3CC0ci}>;DJSs{yu+QzMPGz5zN!aNr{(@|Z* zV@q9x=!FCEsXDXM&)AxjZIuBz7r26$feRBPV++SN1_WJuL8>Bpj^fv|sR;vvCsg`Y zotcjVc|L~E_3`i>h4p-6s4K4XlL|<@KCc9|JyFy&2Qlhmq$BAhRdGfTJ?^_#6GJDd zH7p_`5Hn-$^hC#}n6!VmS}^O6@?EJY+@Cl(2^54q{K@0aGK;P&D|fcXG$!|J0&Ff; z1Q>qaOEc6yMVj;O^KlpZPB7;^EK*j8lJ{F_R#vwl!NtO|G1EkZ>9Q>Yqjd3}>y&Fw z&?<#&Me~E_Ng4O`n}`%G({^L83f6BvZKui%gfc_7+8FTTbT@_Kbx+oQHCvp~l2@hp zn2Y^(>U0eEY5YMi(w>;(W80EMwrlvB&w^a2gAUsmLujq9DxlwXAEIW?tg!XUmmtEiR00A z$(2Y@(VtvbkIhj=f#mD7odQ}G>$LlEKzL?blWKZwbS|0`rT zGDz)@_6EHr#QtA(G8a{yQG|AwXr+w}^(e8dfBOVsv*HL6oNFad<-52{(3kCD2_Lb- zDLC@So%sFQz(kIg42>DmLMWluCln@LsmqZeOD0u3+@`-s$CKNH`Q@yaM3>uw4yX|I z6#msrXV5kEF>ShrlD?)=jZu8$iAV{+HJZaDg2Bc%r;mQ?i?_?55s?3QaWJHmZE*-{ zoc!IkcO@dr^k|a0%+AM7dyUgcYh%#e4^GQ|ir_j0C)amVyj9U$+FCAV)jk)V;a*;eWzGt16DGMLD`Gc@kW47H;^iU;dG z6z(7zSr3pFC1<*?1gY#k`^2Hesm0?43r*amk4e2LPdz6L5LKtq(eb0r4qAGchFzk) z;eWQ1Q;M&A-c;gKRU77z=tMw#QwF704o)x4^ikEsbM(8Rw=LjI(@M_CJYla=_#yAO znJ=(-n0+e7Ml7=>C*xJQ@CqlB95!xU?!*$#Q#aTIWR#T|GdNH6)4l#v7>7Am##tMc(}p^bb{FyCzCuSk%&3g3 hOV!!SxrbVDxW14>$)H0&&EiKR<2)bGnJ>O^;Qyz5z?lF5 diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json new file mode 100644 index 00000000000..a8544f7dc02 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-101a4299cad9e06288ea7a290a730a1c696ab53d03e0b241f35f9d002eee3233.json @@ -0,0 +1 @@ +aa8fcaad83dc96339d97ce135e408ca1eb5f7392ade678e224856b394b6370b2fa522a002acb149020e17d3bbbe6444e \ No newline at end of file diff --git a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json b/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json deleted file mode 100644 index e048700d163..00000000000 --- a/packages/rs-sdk/tests/vectors/test_fetch_one_group_action_since_existing_one_with_limit/quorum_pubkey-106-1f0a25d463a2912cd31dd4f91b899a143d6ae990bb9bc89cb34f7c5db8b1b705.json +++ /dev/null @@ -1 +0,0 @@ -acbfb39d5f22cd2f096af600d2f19d618fa9898fecd778393c777d1b1785ee5fbad857cf62b3b69ade96894bafe42c7c \ No newline at end of file From 00134b7762ecc46cd58898cfa2c00d8c86c6b64a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 13 May 2025 01:32:11 +0700 Subject: [PATCH 5/9] more work --- .../src/state_transition/proof_result.rs | 10 ++ .../batch/tests/token/mint/mod.rs | 41 ++++++ .../group/fetch/fetch_action_is_closed/mod.rs | 49 +++++++ .../fetch/fetch_action_is_closed/v0/mod.rs | 46 +++++++ .../rs-drive/src/drive/group/fetch/mod.rs | 1 + .../group/insert/add_group_action/v0/mod.rs | 42 +++++- .../v0/transformer.rs | 44 +++++++ .../util/grove_operations/batch_delete/mod.rs | 44 +++++++ .../grove_operations/batch_delete/v0/mod.rs | 27 +++- .../v0/mod.rs | 124 +++++++++++++++++- .../drive_group_method_versions/mod.rs | 1 + .../drive_group_method_versions/v1.rs | 1 + 12 files changed, 424 insertions(+), 6 deletions(-) create mode 100644 packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/mod.rs create mode 100644 packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/v0/mod.rs diff --git a/packages/rs-dpp/src/state_transition/proof_result.rs b/packages/rs-dpp/src/state_transition/proof_result.rs index 248629015d2..b35c3503309 100644 --- a/packages/rs-dpp/src/state_transition/proof_result.rs +++ b/packages/rs-dpp/src/state_transition/proof_result.rs @@ -27,6 +27,16 @@ pub enum StateTransitionProofResult { VerifiedTokenActionWithDocument(Document), VerifiedTokenGroupActionWithDocument(GroupSumPower, Option), VerifiedTokenGroupActionWithTokenBalance(GroupSumPower, GroupActionStatus, Option), + VerifiedTokenGroupActionWithTokenIdentityInfo( + GroupSumPower, + GroupActionStatus, + Option, + ), + VerifiedTokenGroupActionWithTokenPricingSchedule( + GroupSumPower, + GroupActionStatus, + Option, + ), VerifiedMasternodeVote(Vote), VerifiedNextDistribution(Vote), } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index a0d83b252e5..07dc634972a 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -1646,6 +1646,47 @@ mod token_mint_tests { .unwrap() .expect("expected to commit transaction"); + // Let's verify the proof of the state transition + + let proof = platform + .drive + .prove_state_transition(&confirm_token_mint_transition, None, platform_version) + .expect("expect to prove state transition"); + + let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof( + &confirm_token_mint_transition, + &BlockInfo::default(), + proof.data.as_ref().expect("expected data"), + &|_| Ok(Some(contract.clone().into())), + platform_version, + ) + .expect( + format!( + "expect to verify state transition proof {}", + hex::encode(&proof.data.expect("expected data")) + ) + .as_str(), + ); + + if keeps_minting_history { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithDocument(power, doc) => { + assert_eq!(power, 2); + assert_eq!(doc.expect("expected to get doc").properties().get_u64("amount"), Ok(1337)); + } + ); + } else { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithTokenBalance(power, status, balance) => { + assert_eq!(power, 2); + assert_eq!(status, GroupActionStatus::ActionClosed); + assert_eq!(balance, Some(101337)); + } + ); + } + let token_balance = platform .drive .fetch_identity_token_balance( diff --git a/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/mod.rs b/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/mod.rs new file mode 100644 index 00000000000..e5d3bb7e29a --- /dev/null +++ b/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/mod.rs @@ -0,0 +1,49 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use dpp::data_contract::GroupContractPosition; +use dpp::identifier::Identifier; +use grovedb::TransactionArg; +use platform_version::version::PlatformVersion; + +impl Drive { + /// Returns **`true`** if the action has already been closed, **`false`** if it is still + /// active. + /// + /// *Errors* if the action is missing or if the platform‑version number is unknown. + #[allow(clippy::too_many_arguments)] + pub fn fetch_action_is_closed( + &self, + contract_id: Identifier, + group_contract_position: GroupContractPosition, + action_id: Identifier, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive + .methods + .group + .fetch + .fetch_action_is_closed + { + 0 => self.fetch_action_is_closed_v0( + contract_id, + group_contract_position, + action_id, + transaction, + drive_operations, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "fetch_action_is_closed".to_owned(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/v0/mod.rs b/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/v0/mod.rs new file mode 100644 index 00000000000..efd53789dc2 --- /dev/null +++ b/packages/rs-drive/src/drive/group/fetch/fetch_action_is_closed/v0/mod.rs @@ -0,0 +1,46 @@ +use crate::drive::group::paths::group_closed_action_root_path; +use crate::drive::Drive; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use crate::util::grove_operations::DirectQueryType; +use dpp::data_contract::GroupContractPosition; +use dpp::identifier::Identifier; +use grovedb::TransactionArg; +use platform_version::version::PlatformVersion; + +impl Drive { + /// V0 implementation — checks for the presence of the action tree under the *closed* root + /// first; if absent, checks the *active* root. + /// Fails if the action is missing from **both** roots. + pub(super) fn fetch_action_is_closed_v0( + &self, + contract_id: Identifier, + group_contract_position: GroupContractPosition, + action_id: Identifier, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result { + let pos_bytes = group_contract_position.to_be_bytes(); + let key = action_id.as_slice(); + + // 1. Does the action exist under the *closed* root? + let closed_root = + group_closed_action_root_path(contract_id.as_slice(), pos_bytes.as_slice()); + if self + .grove_get_raw_optional( + (&closed_root).into(), + key, + DirectQueryType::StatefulDirectQuery, + transaction, + drive_operations, + &platform_version.drive, + )? + .is_some() + { + return Ok(true); + } + + Ok(false) + } +} diff --git a/packages/rs-drive/src/drive/group/fetch/mod.rs b/packages/rs-drive/src/drive/group/fetch/mod.rs index 473e9dfa488..339edb03724 100644 --- a/packages/rs-drive/src/drive/group/fetch/mod.rs +++ b/packages/rs-drive/src/drive/group/fetch/mod.rs @@ -2,6 +2,7 @@ mod fetch_action_id_has_signer; mod fetch_action_id_info_keep_serialized; mod fetch_action_id_signers_power; mod fetch_action_infos; +mod fetch_action_is_closed; mod fetch_action_signers; mod fetch_active_action_info; mod fetch_group_info; diff --git a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs index b49959ab9c0..084c86d895b 100644 --- a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs @@ -8,7 +8,8 @@ use crate::error::drive::DriveError; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; use crate::util::grove_operations::{ - BatchInsertApplyType, BatchInsertTreeApplyType, BatchMoveApplyType, QueryTarget, + BatchDeleteApplyType, BatchInsertApplyType, BatchInsertTreeApplyType, BatchMoveApplyType, + QueryTarget, }; use crate::util::object_size_info::PathKeyInfo::PathFixedSizeKeyRef; use crate::util::object_size_info::{DriveKeyInfo, PathKeyElementInfo}; @@ -24,7 +25,7 @@ use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::element::SumValue; use grovedb::MaybeTree::NotTree; -use grovedb::{Element, EstimatedLayerInformation, TransactionArg, TreeType}; +use grovedb::{Element, EstimatedLayerInformation, MaybeTree, TransactionArg, TreeType}; use grovedb_epoch_based_storage_flags::StorageFlags; use std::collections::HashMap; @@ -385,6 +386,11 @@ impl Drive { } }; + let group_active_action_root_path = group_active_action_root_path( + contract_id.as_slice(), + group_contract_position_bytes.as_slice(), + ); + let group_active_action_path = group_active_action_path( contract_id.as_slice(), group_contract_position_bytes.as_slice(), @@ -407,6 +413,38 @@ impl Drive { &mut batch_operations, &platform_version.drive, )?; + + // We then need to then do a delete operation of the action in the active part + + let delete_apply_type = if estimated_costs_only_with_layer_info.is_none() { + BatchDeleteApplyType::StatefulBatchDelete { + is_known_to_be_subtree_with_sum: Some(NotTree), + } + } else { + BatchDeleteApplyType::StatefulBatchDelete { + is_known_to_be_subtree_with_sum: Some(MaybeTree::Tree( + TreeType::NormalTree, + )), + } + }; + + self.batch_delete( + group_active_action_path.as_slice().into(), + ACTION_SIGNERS_KEY, + delete_apply_type, + transaction, + &mut batch_operations, + &platform_version.drive, + )?; + + self.batch_delete( + group_active_action_root_path.as_slice().into(), + action_id.as_slice(), + delete_apply_type, + transaction, + &mut batch_operations, + &platform_version.drive, + )?; } } diff --git a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs index 9beacb89a5d..6c447595795 100644 --- a/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs +++ b/packages/rs-drive/src/state_transition_action/batch/batched_transition/token_transition/token_base_transition_action/v0/transformer.rs @@ -67,6 +67,28 @@ impl TokenBaseTransitionActionV0 { let current_power = if action_is_proposer { 0 } else { + let closed = drive.fetch_action_is_closed( + data_contract_id, + group_contract_position, + action_id, + transaction, + drive_operations, + platform_version, + )?; + + if closed { + return Ok(ConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::GroupActionAlreadyCompletedError( + GroupActionAlreadyCompletedError::new( + data_contract_id, + group_contract_position, + action_id, + ), + ), + ), + )); + } match drive.fetch_action_id_signers_power_and_add_operations( data_contract_id, group_contract_position, @@ -179,6 +201,28 @@ impl TokenBaseTransitionActionV0 { let current_power = if *action_is_proposer { 0 } else { + let closed = drive.fetch_action_is_closed( + *data_contract_id, + *group_contract_position, + *action_id, + transaction, + drive_operations, + platform_version, + )?; + + if closed { + return Ok(ConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::GroupActionAlreadyCompletedError( + GroupActionAlreadyCompletedError::new( + *data_contract_id, + *group_contract_position, + *action_id, + ), + ), + ), + )); + } match drive.fetch_action_id_signers_power_and_add_operations( *data_contract_id, *group_contract_position, diff --git a/packages/rs-drive/src/util/grove_operations/batch_delete/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_delete/mod.rs index 744e1a47ab4..78199741aab 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_delete/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_delete/mod.rs @@ -5,6 +5,7 @@ use crate::error::drive::DriveError; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; use crate::util::grove_operations::BatchDeleteApplyType; +use grovedb::operations::delete::DeleteOptions; use dpp::version::drive_versions::DriveVersion; @@ -50,4 +51,47 @@ impl Drive { })), } } + + /// Pushes a "delete element" operation to `drive_operations`. + /// + /// # Parameters + /// * `path`: The path to delete. + /// * `key`: The key of the item to delete. + /// * `apply_type`: The apply type for the delete operation. + /// * `options`: The delete options. + /// * `transaction`: The transaction argument. + /// * `drive_operations`: The vector containing low-level drive operations. + /// * `drive_version`: The drive version to select the correct function version to run. + /// + /// # Returns + /// * `Ok(())` if the operation was successful. + /// * `Err(DriveError::UnknownVersionMismatch)` if the drive version does not match known versions. + #[allow(clippy::too_many_arguments)] + pub fn batch_delete_with_options>( + &self, + path: SubtreePath<'_, B>, + key: &[u8], + apply_type: BatchDeleteApplyType, + options: DeleteOptions, + transaction: TransactionArg, + drive_operations: &mut Vec, + drive_version: &DriveVersion, + ) -> Result<(), Error> { + match drive_version.grove_methods.batch.batch_delete { + 0 => self.batch_delete_with_options_v0( + path, + key, + apply_type, + options, + transaction, + drive_operations, + drive_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "batch_delete".to_string(), + known_versions: vec![0], + received: version, + })), + } + } } diff --git a/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs index 7b28657228c..a1b7680314f 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_delete/v0/mod.rs @@ -22,14 +22,37 @@ impl Drive { drive_operations: &mut Vec, drive_version: &DriveVersion, ) -> Result<(), Error> { - let current_batch_operations = - LowLevelDriveOperation::grovedb_operations_batch(drive_operations); let options = DeleteOptions { allow_deleting_non_empty_trees: false, deleting_non_empty_trees_returns_error: true, base_root_storage_is_free: true, validate_tree_at_path_exists: false, //todo: not sure about this one }; + self.batch_delete_with_options_v0( + path, + key, + apply_type, + options, + transaction, + drive_operations, + drive_version, + ) + } + /// Pushes a "delete element" operation to `drive_operations`. + #[allow(clippy::too_many_arguments)] + pub(super) fn batch_delete_with_options_v0>( + &self, + path: SubtreePath<'_, B>, + key: &[u8], + apply_type: BatchDeleteApplyType, + options: DeleteOptions, + transaction: TransactionArg, + drive_operations: &mut Vec, + drive_version: &DriveVersion, + ) -> Result<(), Error> { + let current_batch_operations = + LowLevelDriveOperation::grovedb_operations_batch(drive_operations); + let delete_operation = match apply_type { BatchDeleteApplyType::StatelessBatchDelete { in_tree_type: is_sum_tree, diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index c721d16c2da..0e810ac0898 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -41,7 +41,7 @@ use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_m use dpp::state_transition::batch_transition::token_unfreeze_transition::v0::v0_methods::TokenUnfreezeTransitionV0Methods; use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; use dpp::state_transition::proof_result::StateTransitionProofResult; -use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenGroupActionWithDocument, VerifiedTokenGroupActionWithTokenBalance, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; +use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenGroupActionWithDocument, VerifiedTokenGroupActionWithTokenBalance, VerifiedTokenGroupActionWithTokenIdentityInfo, VerifiedTokenGroupActionWithTokenPricingSchedule, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenPricingSchedule}; use dpp::system_data_contracts::{load_system_data_contract, SystemDataContract}; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; use dpp::voting::vote_polls::VotePoll; @@ -480,11 +480,14 @@ impl Drive { platform_version, )?; + let recipient_id = + token_mint_transition.recipient_id(token_config)?; + let (root_hash, balance) = Drive::verify_token_balance_for_identity_id( proof, token_id.into_buffer(), - owner_id.into_buffer(), + recipient_id.into_buffer(), true, platform_version, )?; @@ -549,6 +552,46 @@ impl Drive { TokenTransition::Freeze(token_freeze_transition) => { if keeps_historical_document.keeps_freezing_history() { historical_query() + } else if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { + let (_root_hash, status, sum_power) = + Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + None, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )?; + + let (root_hash, identity_token_info) = + Drive::verify_token_info_for_identity_id( + proof, + token_id.into_buffer(), + token_freeze_transition + .frozen_identity_id() + .into_buffer(), + false, + platform_version, + )?; + if status == GroupActionStatus::ActionClosed + && identity_token_info.is_none() + { + return Err(Error::Proof(ProofError::IncorrectProof( + format!("proof did not contain token identity info for identity {} expected to exist because of state transition (token freeze)", owner_id)))); + }; + + Ok(( + root_hash, + VerifiedTokenGroupActionWithTokenIdentityInfo( + sum_power, + status, + identity_token_info, + ), + )) } else { let (root_hash, Some(identity_token_info)) = Drive::verify_token_info_for_identity_id( @@ -577,6 +620,46 @@ impl Drive { TokenTransition::Unfreeze(token_unfreeze_transition) => { if keeps_historical_document.keeps_freezing_history() { historical_query() + } else if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { + let (_root_hash, status, sum_power) = + Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + None, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )?; + + let (root_hash, identity_token_info) = + Drive::verify_token_info_for_identity_id( + proof, + token_id.into_buffer(), + token_unfreeze_transition + .frozen_identity_id() + .into_buffer(), + false, + platform_version, + )?; + if status == GroupActionStatus::ActionClosed + && identity_token_info.is_none() + { + return Err(Error::Proof(ProofError::IncorrectProof( + format!("proof did not contain token identity info for identity {} expected to exist because of state transition (token unfreeze)", owner_id)))); + }; + + Ok(( + root_hash, + VerifiedTokenGroupActionWithTokenIdentityInfo( + sum_power, + status, + identity_token_info, + ), + )) } else { let (root_hash, Some(identity_token_info)) = Drive::verify_token_info_for_identity_id( @@ -624,6 +707,43 @@ impl Drive { TokenTransition::SetPriceForDirectPurchase(_) => { if keeps_historical_document.keeps_direct_pricing_history() { historical_query() + } else if let Some(group_state_transition_info) = + token_transition.base().using_group_info() + { + let (_root_hash, status, sum_power) = + Drive::verify_action_signer_and_total_power( + proof, + data_contract_id, + group_state_transition_info.group_contract_position, + None, + group_state_transition_info.action_id, + owner_id, + true, + platform_version, + )?; + + let (root_hash, token_pricing_schedule) = + Drive::verify_token_direct_selling_price( + proof, + token_id.into_buffer(), + false, + platform_version, + )?; + if status == GroupActionStatus::ActionClosed + && token_pricing_schedule.is_none() + { + return Err(Error::Proof(ProofError::IncorrectProof( + format!("proof did not contain token identity info for identity {} expected to exist because of state transition (token set price for direct purchase)", owner_id)))); + }; + + Ok(( + root_hash, + VerifiedTokenGroupActionWithTokenPricingSchedule( + sum_power, + status, + token_pricing_schedule, + ), + )) } else { let (root_hash, token_pricing_schedule) = Drive::verify_token_direct_selling_price( diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/mod.rs index c2a337ebe41..5ac8ff2fc63 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/mod.rs @@ -20,6 +20,7 @@ pub struct DriveGroupFetchMethodVersions { pub fetch_group_infos: FeatureVersion, pub fetch_action_infos: FeatureVersion, pub fetch_action_signers: FeatureVersion, + pub fetch_action_is_closed: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/v1.rs b/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/v1.rs index e6ee9e129f7..409f44fd937 100644 --- a/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/v1.rs +++ b/packages/rs-platform-version/src/version/drive_versions/drive_group_method_versions/v1.rs @@ -13,6 +13,7 @@ pub const DRIVE_GROUP_METHOD_VERSIONS_V1: DriveGroupMethodVersions = DriveGroupM fetch_group_infos: 0, fetch_action_infos: 0, fetch_action_signers: 0, + fetch_action_is_closed: 0, }, prove: DriveGroupProveMethodVersions { prove_group_info: 0, From d15536173f6ed3651f58a7e2c764c8f094a575c4 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 13 May 2025 04:33:39 +0700 Subject: [PATCH 6/9] more tests and fixes --- .../batch/tests/token/freeze/mod.rs | 1432 +++++++++++++++++ .../batch/tests/token/mint/mod.rs | 18 +- .../batch/tests/token/mod.rs | 851 +--------- .../group/prove/prove_action_infos/v0/mod.rs | 2 +- .../grove_operations/batch_move/v0/mod.rs | 6 +- .../v0/mod.rs | 12 +- .../token_transition.rs | 10 +- .../v0/mod.rs | 6 +- 8 files changed, 1461 insertions(+), 876 deletions(-) create mode 100644 packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs new file mode 100644 index 00000000000..a318eaf4b39 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/freeze/mod.rs @@ -0,0 +1,1432 @@ +use super::*; +mod token_freeze_tests { + use super::*; + use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + + mod token_freeze_basic_tests { + use super::*; + #[test] + fn test_token_freeze() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_freeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + None, + platform_version, + ); + + let freeze_transition = BatchTransition::new_token_freeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + identity_2.id(), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let freeze_serialized_transition = freeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(true)); + } + + #[test] + fn test_token_freeze_and_unfreeze() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_freeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + token_configuration.set_unfreeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + None, + platform_version, + ); + + let freeze_transition = BatchTransition::new_token_freeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + identity_2.id(), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let freeze_serialized_transition = freeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(true)); + + let unfreeze_transition = BatchTransition::new_token_unfreeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + identity_2.id(), + None, + None, + &key, + 3, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let unfreeze_serialized_transition = unfreeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![unfreeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + identity_2.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(false)); + } + + #[test] + fn test_token_frozen_receive_balance_allowed_sending_not_allowed_till_unfrozen() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (recipient, signer2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_freeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + token_configuration.set_unfreeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + None, + platform_version, + ); + + let freeze_transition = BatchTransition::new_token_freeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + recipient.id(), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let freeze_serialized_transition = freeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(true)); + + let token_transfer_transition = BatchTransition::new_token_transfer_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + recipient.id(), + None, + None, + None, + &key, + 3, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_serialized_transition = token_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![token_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 100000 - 1337; + assert_eq!(token_balance, Some(expected_amount)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 1337; + assert_eq!(token_balance, Some(expected_amount)); + + //now let's try sending our balance + + let token_transfer_back_transition = BatchTransition::new_token_transfer_transition( + token_id, + recipient.id(), + contract.id(), + 0, + 300, + identity.id(), + None, + None, + None, + &key2, + 2, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_back_serialized_transition = token_transfer_back_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![token_transfer_back_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::IdentityTokenAccountFrozenError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // We expect no change + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 100000 - 1337; + assert_eq!(token_balance, Some(expected_amount)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 1337; + assert_eq!(token_balance, Some(expected_amount)); + + let unfreeze_transition = BatchTransition::new_token_unfreeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + recipient.id(), + None, + None, + &key, + 4, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let unfreeze_serialized_transition = unfreeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![unfreeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(false)); + + let token_transfer_transition = BatchTransition::new_token_transfer_transition( + token_id, + recipient.id(), + contract.id(), + 0, + 300, + identity.id(), + None, + None, + None, + &key2, + 3, + 0, + &signer2, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_serialized_transition = token_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![token_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 100000 - 1337 + 300; + assert_eq!(token_balance, Some(expected_amount)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 1337 - 300; + assert_eq!(token_balance, Some(expected_amount)); + } + + #[test] + fn test_token_frozen_receive_balance_may_not_be_allowed() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (recipient, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.allow_transfer_to_frozen_balance(false); + + token_configuration.set_freeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + token_configuration.set_unfreeze_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + None, + platform_version, + ); + + let freeze_transition = BatchTransition::new_token_freeze_transition( + token_id, + identity.id(), + contract.id(), + 0, + recipient.id(), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let freeze_serialized_transition = freeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token info") + .map(|info| info.frozen()); + assert_eq!(token_frozen, Some(true)); + + let token_transfer_transition = BatchTransition::new_token_transfer_transition( + token_id, + identity.id(), + contract.id(), + 0, + 1337, + recipient.id(), + None, + None, + None, + &key, + 3, + 0, + &signer, + platform_version, + None, + ) + .expect("expect to create documents batch transition"); + + let token_transfer_serialized_transition = token_transfer_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![token_transfer_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::IdentityTokenAccountFrozenError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + let expected_amount = 100000; + assert_eq!(token_balance, Some(expected_amount)); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + recipient.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, None); + } + } + + mod token_freeze_group_tests { + use super::*; + use dpp::data_contract::associated_token::token_keeps_history_rules::accessors::v0::TokenKeepsHistoryRulesV0Setters; + use dpp::group::group_action_status::GroupActionStatus; + use dpp::state_transition::batch_transition::TokenFreezeTransition; + use dpp::state_transition::proof_result::StateTransitionProofResult; + use dpp::tokens::info::v0::IdentityTokenInfoV0; + use dpp::tokens::info::IdentityTokenInfo; + use drive::drive::Drive; + // ────────────────────────────────────────────────────────── + // Owner tries to freeze, but authorization is *group‑only* + // and owner does NOT hold enough power alone → Error. + // ────────────────────────────────────────────────────────── + #[test] + fn test_token_freeze_owner_not_authorized_group_required() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(42); + let platform_state = platform.state.load(); + + let (owner, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (member, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + // Contract where freeze / unfreeze require Group(0) + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + owner.id(), + Some(|token_cfg: &mut TokenConfiguration| { + token_cfg.set_freeze_rules(ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + ..Default::default() + })); + token_cfg.set_unfreeze_rules(ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + ..Default::default() + })); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(owner.id(), 1), (member.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + // Owner proposes freeze outside of a group action + let freeze = BatchTransition::new_token_freeze_transition( + token_id, + owner.id(), + contract.id(), + 0, + member.id(), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + ) + .expect("create freeze"); + + let serialized = freeze.serialize_to_bytes().expect("serialize freeze"); + + let tx = platform.drive.grove.start_transaction(); + let result = platform + .platform + .process_raw_state_transitions( + &[serialized], + &platform_state, + &BlockInfo::default(), + &tx, + platform_version, + false, + None, + ) + .expect("process"); + + assert_matches!( + result.execution_results().as_slice(), + [PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + } + + // ────────────────────────────────────────────────────────── + // Owner alone HAS enough power in the group (5 ≥ required 5) + // → freeze succeeds immediately. + // ────────────────────────────────────────────────────────── + #[test] + fn test_token_freeze_owner_enough_group_power_without_group_action() { + let version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(43); + let state = platform.state.load(); + + let (owner, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (other_identity_1, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (other_identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (target, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + owner.id(), + Some(|cfg: &mut TokenConfiguration| { + cfg.set_freeze_rules(ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + ..Default::default() + })); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [ + (owner.id(), 2), + (other_identity_1.id(), 1), + (other_identity_2.id(), 1), + ] + .into(), + required_power: 2, + }), + )] + .into(), + ), + version, + ); + + let freeze = BatchTransition::new_token_freeze_transition( + token_id, + owner.id(), + contract.id(), + 0, + target.id(), + None, + None, + &key, + 2, + 0, + &signer, + version, + None, + ) + .unwrap(); + + let freeze_ser = freeze.serialize_to_bytes().unwrap(); + let tx = platform.drive.grove.start_transaction(); + let res = platform + .platform + .process_raw_state_transitions( + &[freeze_ser], + &state, + &BlockInfo::default(), + &tx, + version, + false, + None, + ) + .unwrap(); + + assert_matches!( + res.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(tx) + .unwrap() + .unwrap(); + + let frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + target.id().to_buffer(), + None, + version, + ) + .unwrap() + .map(|i| i.frozen()); + assert_eq!(frozen, Some(true)); + } + + // ────────────────────────────────────────────────────────── + // Owner alone HAS enough power in the group (5 ≥ required 5) + // → freeze succeeds immediately. + // ────────────────────────────────────────────────────────── + #[test] + fn test_token_freeze_owner_enough_group_power_using_group_action() { + let version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(43); + let state = platform.state.load(); + + let (owner, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (other_identity_1, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (other_identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (target, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + owner.id(), + Some(|cfg: &mut TokenConfiguration| { + cfg.set_freeze_rules(ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + ..Default::default() + })); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [ + (owner.id(), 2), + (other_identity_1.id(), 1), + (other_identity_2.id(), 1), + ] + .into(), + required_power: 2, + }), + )] + .into(), + ), + version, + ); + + let freeze = BatchTransition::new_token_freeze_transition( + token_id, + owner.id(), + contract.id(), + 0, + target.id(), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + version, + None, + ) + .unwrap(); + + let freeze_ser = freeze.serialize_to_bytes().unwrap(); + let tx = platform.drive.grove.start_transaction(); + let res = platform + .platform + .process_raw_state_transitions( + &[freeze_ser], + &state, + &BlockInfo::default(), + &tx, + version, + false, + None, + ) + .unwrap(); + + assert_matches!( + res.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(tx) + .unwrap() + .unwrap(); + + let frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + target.id().to_buffer(), + None, + version, + ) + .unwrap() + .map(|i| i.frozen()); + assert_eq!(frozen, Some(true)); + } + + #[test] + fn test_token_freeze_two_member_group_no_keeping_history() { + test_token_freeze_two_member_group_with_keeps_history(false); + } + + #[test] + fn test_token_freeze_two_member_group_keeping_history() { + test_token_freeze_two_member_group_with_keeps_history(true); + } + + // ────────────────────────────────────────────────────────── + // Two‑signer scenario: proposer + second member complete freeze + // ────────────────────────────────────────────────────────── + fn test_token_freeze_two_member_group_with_keeps_history(keeps_freezing_history: bool) { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(44); + let platform_state = platform.state.load(); + + let (id1, sign1, key1) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (id2, sign2, key2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let (target, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + id1.id(), + Some(|cfg: &mut TokenConfiguration| { + cfg.keeps_history_mut() + .set_keeps_freezing_history(keeps_freezing_history); + cfg.set_freeze_rules(ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + ..Default::default() + })); + }), + None, + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(id1.id(), 1), (id2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + // proposer + let token_freeze_transition = BatchTransition::new_token_freeze_transition( + token_id, + id1.id(), + contract.id(), + 0, + target.id(), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key1, + 2, + 0, + &sign1, + platform_version, + None, + ) + .expect("expect to create batch transition"); + + let token_freeze_serialized_transition = token_freeze_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Prove & verify + let proof = platform + .drive + .prove_state_transition(&token_freeze_transition, None, platform_version) + .expect("expect to prove state transition"); + let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof( + &token_freeze_transition, + &BlockInfo::default(), + proof.data.as_ref().expect("expected data"), + &|_| Ok(Some(contract.clone().into())), + platform_version, + ) + .unwrap_or_else(|e| { + panic!( + "expect to verify state transition proof {}, error is {}", + hex::encode(proof.data.expect("expected data")), + e + ) + }); + + if keeps_freezing_history { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithDocument(power, doc) => { + assert_eq!(power, 1); + assert_eq!(doc, None); + } + ); + } else { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithTokenIdentityInfo(power, status, info) => { + assert_eq!(power, 1); + assert_eq!(status, GroupActionStatus::ActionActive); + assert_eq!(info, None); + } + ); + } + + // second signer + let action_id = TokenFreezeTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + id1.id().as_bytes(), + 2, + target.id().as_bytes(), + ); + + let token_freeze_confirm_transition = BatchTransition::new_token_freeze_transition( + token_id, + id2.id(), + contract.id(), + 0, + target.id(), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key2, + 2, + 0, + &sign2, + platform_version, + None, + ) + .unwrap(); + + let token_freeze_serialized_transition = token_freeze_confirm_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + let processing_result = platform + .platform + .process_raw_state_transitions( + &[token_freeze_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Prove & verify + let proof = platform + .drive + .prove_state_transition(&token_freeze_confirm_transition, None, platform_version) + .expect("expect to prove state transition"); + let (_root_hash, result) = Drive::verify_state_transition_was_executed_with_proof( + &token_freeze_confirm_transition, + &BlockInfo::default(), + proof.data.as_ref().expect("expected data"), + &|_| Ok(Some(contract.clone().into())), + platform_version, + ) + .unwrap_or_else(|e| { + panic!( + "expect to verify state transition proof {}, error is {}", + hex::encode(proof.data.expect("expected data")), + e + ) + }); + + if keeps_freezing_history { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithDocument(power, doc) => { + assert_eq!(power, 2); + assert_eq!(doc.expect("expected document").properties().get_identifier("frozenIdentityId").expect("expected frozen id"), target.id()); + } + ); + } else { + assert_matches!( + result, + StateTransitionProofResult::VerifiedTokenGroupActionWithTokenIdentityInfo(power, status, info) => { + assert_eq!(power, 2); + assert_eq!(status, GroupActionStatus::ActionClosed); + assert_eq!(info, Some(IdentityTokenInfo::V0(IdentityTokenInfoV0 { frozen: true }))); + } + ); + } + + // Verify frozen + let frozen = platform + .drive + .fetch_identity_token_info( + token_id.to_buffer(), + target.id().to_buffer(), + None, + platform_version, + ) + .unwrap() + .map(|i| i.frozen()); + assert_eq!(frozen, Some(true)); + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs index 07dc634972a..a1a365cb039 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mint/mod.rs @@ -1544,13 +1544,12 @@ mod token_mint_tests { &|_| Ok(Some(contract.clone().into())), platform_version, ) - .expect( - format!( + .unwrap_or_else(|_| { + panic!( "expect to verify state transition proof {}", - hex::encode(&proof.data.expect("expected data")) + hex::encode(proof.data.expect("expected data")) ) - .as_str(), - ); + }); if keeps_minting_history { assert_matches!( @@ -1660,13 +1659,12 @@ mod token_mint_tests { &|_| Ok(Some(contract.clone().into())), platform_version, ) - .expect( - format!( + .unwrap_or_else(|_| { + panic!( "expect to verify state transition proof {}", - hex::encode(&proof.data.expect("expected data")) + hex::encode(proof.data.expect("expected data")) ) - .as_str(), - ); + }); if keeps_minting_history { assert_matches!( diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs index 69827d1ef3d..cdaef933c99 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/mod.rs @@ -1,5 +1,6 @@ mod direct_selling; mod distribution; +mod freeze; mod mint; use super::*; @@ -1393,856 +1394,6 @@ mod token_tests { } } - mod token_freeze_tests { - use super::*; - use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; - - #[test] - fn test_token_freeze() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_freeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - None, - platform_version, - ); - - let freeze_transition = BatchTransition::new_token_freeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - identity_2.id(), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let freeze_serialized_transition = freeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![freeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(true)); - } - - #[test] - fn test_token_freeze_and_unfreeze() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (identity_2, _, _) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_freeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - token_configuration.set_unfreeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - None, - platform_version, - ); - - let freeze_transition = BatchTransition::new_token_freeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - identity_2.id(), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let freeze_serialized_transition = freeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![freeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(true)); - - let unfreeze_transition = BatchTransition::new_token_unfreeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - identity_2.id(), - None, - None, - &key, - 3, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let unfreeze_serialized_transition = unfreeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![unfreeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - identity_2.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(false)); - } - - #[test] - fn test_token_frozen_receive_balance_allowed_sending_not_allowed_till_unfrozen() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (recipient, signer2, key2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_freeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - token_configuration.set_unfreeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - None, - platform_version, - ); - - let freeze_transition = BatchTransition::new_token_freeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - recipient.id(), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let freeze_serialized_transition = freeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![freeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(true)); - - let token_transfer_transition = BatchTransition::new_token_transfer_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - recipient.id(), - None, - None, - None, - &key, - 3, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_transfer_serialized_transition = token_transfer_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_transfer_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 100000 - 1337; - assert_eq!(token_balance, Some(expected_amount)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 1337; - assert_eq!(token_balance, Some(expected_amount)); - - //now let's try sending our balance - - let token_transfer_back_transition = BatchTransition::new_token_transfer_transition( - token_id, - recipient.id(), - contract.id(), - 0, - 300, - identity.id(), - None, - None, - None, - &key2, - 2, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_transfer_back_serialized_transition = token_transfer_back_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_transfer_back_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::IdentityTokenAccountFrozenError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - // We expect no change - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 100000 - 1337; - assert_eq!(token_balance, Some(expected_amount)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 1337; - assert_eq!(token_balance, Some(expected_amount)); - - let unfreeze_transition = BatchTransition::new_token_unfreeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - recipient.id(), - None, - None, - &key, - 4, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let unfreeze_serialized_transition = unfreeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![unfreeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(false)); - - let token_transfer_transition = BatchTransition::new_token_transfer_transition( - token_id, - recipient.id(), - contract.id(), - 0, - 300, - identity.id(), - None, - None, - None, - &key2, - 3, - 0, - &signer2, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_transfer_serialized_transition = token_transfer_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_transfer_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 100000 - 1337 + 300; - assert_eq!(token_balance, Some(expected_amount)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 1337 - 300; - assert_eq!(token_balance, Some(expected_amount)); - } - - #[test] - fn test_token_frozen_receive_balance_may_not_be_allowed() { - let platform_version = PlatformVersion::latest(); - let mut platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_genesis_state(); - - let mut rng = StdRng::seed_from_u64(49853); - - let platform_state = platform.state.load(); - - let (identity, signer, key) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (recipient, _, _) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - - let (contract, token_id) = create_token_contract_with_owner_identity( - &mut platform, - identity.id(), - Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.allow_transfer_to_frozen_balance(false); - - token_configuration.set_freeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - token_configuration.set_unfreeze_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, - admin_action_takers: AuthorizedActionTakers::NoOne, - changing_authorized_action_takers_to_no_one_allowed: false, - changing_admin_action_takers_to_no_one_allowed: false, - self_changing_admin_action_takers_allowed: false, - }, - )); - }), - None, - None, - platform_version, - ); - - let freeze_transition = BatchTransition::new_token_freeze_transition( - token_id, - identity.id(), - contract.id(), - 0, - recipient.id(), - None, - None, - &key, - 2, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let freeze_serialized_transition = freeze_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![freeze_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_frozen = platform - .drive - .fetch_identity_token_info( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token info") - .map(|info| info.frozen()); - assert_eq!(token_frozen, Some(true)); - - let token_transfer_transition = BatchTransition::new_token_transfer_transition( - token_id, - identity.id(), - contract.id(), - 0, - 1337, - recipient.id(), - None, - None, - None, - &key, - 3, - 0, - &signer, - platform_version, - None, - ) - .expect("expect to create documents batch transition"); - - let token_transfer_serialized_transition = token_transfer_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); - - let transaction = platform.drive.grove.start_transaction(); - - let processing_result = platform - .platform - .process_raw_state_transitions( - &vec![token_transfer_serialized_transition.clone()], - &platform_state, - &BlockInfo::default(), - &transaction, - platform_version, - false, - None, - ) - .expect("expected to process state transition"); - - assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::IdentityTokenAccountFrozenError(_)), - _ - )] - ); - - platform - .drive - .grove - .commit_transaction(transaction) - .unwrap() - .expect("expected to commit transaction"); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - identity.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - let expected_amount = 100000; - assert_eq!(token_balance, Some(expected_amount)); - - let token_balance = platform - .drive - .fetch_identity_token_balance( - token_id.to_buffer(), - recipient.id().to_buffer(), - None, - platform_version, - ) - .expect("expected to fetch token balance"); - assert_eq!(token_balance, None); - } - } - mod token_config_update_tests { use super::*; use dpp::data_contract::accessors::v1::DataContractV1Getters; diff --git a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs index d6b203549fa..eab3d5423fb 100644 --- a/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/prove/prove_action_infos/v0/mod.rs @@ -209,7 +209,7 @@ mod tests { contract_id, group_contract_position, Some(action_2.clone()), - true, + false, action_id_2, identity_2_id, 1, diff --git a/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs index 4afd66cba97..0d9f840a362 100644 --- a/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/batch_move/v0/mod.rs @@ -37,9 +37,7 @@ impl Drive { drive_version, )? .ok_or_else(|| { - Error::Drive(DriveError::ElementNotFound( - "element to move not found".into(), - )) + Error::Drive(DriveError::ElementNotFound("element to move not found")) })?; if element.is_any_tree() { @@ -270,7 +268,7 @@ mod tests { &platform_version.drive, ); - assert_matches!(res, Err(Error::Drive(DriveError::ElementNotFound(_)))); + assert_matches!(res, Err(Error::GroveDB(grovedb::Error::PathKeyNotFound(_)))); } /// Moving a subtree (tree element) must fail with NotSupported. diff --git a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs index 4e30568f913..ecf4665d7ee 100644 --- a/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs +++ b/packages/rs-drive/src/verify/group/verify_action_signers_total_power/v0/mod.rs @@ -62,11 +62,15 @@ impl Drive { "group closed action should be returned".to_string(), ))); }; - if active_root.is_some() && closed_root.is_some() - || active_root.is_none() && closed_root.is_none() - { + if active_root.is_some() && closed_root.is_some() { return Err(Error::Proof(ProofError::CorruptedProof( - "group action should be either active or closed".to_string(), + "group action should be either active or closed, but was both".to_string(), + ))); + } + if active_root.is_none() && closed_root.is_none() { + return Err(Error::Proof(ProofError::CorruptedProof( + "group action should be either active or closed, but was neither" + .to_string(), ))); } if active_root.is_some() { diff --git a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs index 440fc21768e..d1b2470bf6c 100644 --- a/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs +++ b/packages/rs-drive/src/verify/state_transition/state_transition_execution_path_queries/token_transition.rs @@ -14,8 +14,10 @@ use dpp::state_transition::batch_transition::batched_transition::token_transitio TokenTransition, TokenTransitionV0Methods, }; use dpp::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; +use dpp::state_transition::batch_transition::token_freeze_transition::v0::v0_methods::TokenFreezeTransitionV0Methods; use dpp::state_transition::batch_transition::token_mint_transition::v0::v0_methods::TokenMintTransitionV0Methods; use dpp::state_transition::batch_transition::token_transfer_transition::v0::v0_methods::TokenTransferTransitionV0Methods; +use dpp::state_transition::batch_transition::token_unfreeze_transition::v0::v0_methods::TokenUnfreezeTransitionV0Methods; use dpp::system_data_contracts::load_system_data_contract; use grovedb::PathQuery; use platform_version::version::PlatformVersion; @@ -113,23 +115,23 @@ impl TryTransitionIntoPathQuery for TokenTransition { ) } } - TokenTransition::Freeze(_) => { + TokenTransition::Freeze(token_frozen_transition) => { if keeps_historical_document.keeps_freezing_history() { create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_info_for_identity_id_query( token_id.to_buffer(), - owner_id.to_buffer(), + token_frozen_transition.frozen_identity_id().to_buffer(), ) } } - TokenTransition::Unfreeze(_) => { + TokenTransition::Unfreeze(token_unfrozen_transition) => { if keeps_historical_document.keeps_freezing_history() { create_token_historical_document_query(self, owner_id, platform_version)? } else { Drive::token_info_for_identity_id_query( token_id.to_buffer(), - owner_id.to_buffer(), + token_unfrozen_transition.frozen_identity_id().to_buffer(), ) } } diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index 0e810ac0898..d2b9f10b0ff 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -574,7 +574,7 @@ impl Drive { token_freeze_transition .frozen_identity_id() .into_buffer(), - false, + true, platform_version, )?; if status == GroupActionStatus::ActionClosed @@ -642,7 +642,7 @@ impl Drive { token_unfreeze_transition .frozen_identity_id() .into_buffer(), - false, + true, platform_version, )?; if status == GroupActionStatus::ActionClosed @@ -726,7 +726,7 @@ impl Drive { Drive::verify_token_direct_selling_price( proof, token_id.into_buffer(), - false, + true, platform_version, )?; if status == GroupActionStatus::ActionClosed From e937613d2bdf7850d397f3e18ddfa6a8ffa539ec Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 13 May 2025 04:44:04 +0700 Subject: [PATCH 7/9] fix --- .../prove/prove_state_transition/v0/mod.rs | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs index 1a48d91db9e..d5f26b6c62f 100644 --- a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs +++ b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs @@ -41,28 +41,6 @@ fn contract_ids_to_non_historical_path_query(contract_ids: &[Identifier]) -> Pat path_query } -fn create_token_historical_document_query( - token_transition: &TokenTransition, - owner_id: Identifier, - platform_version: &PlatformVersion, -) -> Result { - let token_history_contract = - load_system_data_contract(SystemDataContract::TokenHistory, platform_version)?; - - let query = SingleDocumentDriveQuery { - contract_id: token_history_contract.id().into_buffer(), - document_type_name: token_transition.historical_document_type_name().to_string(), - document_type_keeps_history: false, - document_id: token_transition - .historical_document_id(owner_id) - .to_buffer(), - block_time_ms: None, //None because we want latest - contested_status: SingleDocumentDriveQueryContestedStatus::NotContested, - }; - - query.construct_path_query(platform_version) -} - impl Drive { pub(super) fn prove_state_transition_v0( &self, From 155a288569bbc85dcddb27d189a64ea01cc5bfd3 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 13 May 2025 04:52:58 +0700 Subject: [PATCH 8/9] fix --- .../rs-drive/src/prove/prove_state_transition/v0/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs index d5f26b6c62f..de0af774df4 100644 --- a/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs +++ b/packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs @@ -8,15 +8,12 @@ use crate::query::{ use crate::verify::state_transition::state_transition_execution_path_queries::TryTransitionIntoPathQuery; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; -use dpp::data_contracts::SystemDataContract; use dpp::identifier::Identifier; use dpp::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0; use dpp::state_transition::batch_transition::batched_transition::document_transition::{ DocumentTransition, DocumentTransitionV0Methods, }; -use dpp::state_transition::batch_transition::batched_transition::token_transition::{ - TokenTransition, TokenTransitionV0Methods, -}; +use dpp::state_transition::batch_transition::batched_transition::token_transition::TokenTransitionV0Methods; use dpp::state_transition::batch_transition::batched_transition::BatchedTransitionRef; use dpp::state_transition::batch_transition::document_base_transition::v0::v0_methods::DocumentBaseTransitionV0Methods; use dpp::state_transition::batch_transition::document_create_transition::v0::v0_methods::DocumentCreateTransitionV0Methods; @@ -27,7 +24,6 @@ use dpp::state_transition::identity_topup_transition::accessors::IdentityTopUpTr use dpp::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0; use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; use dpp::state_transition::{StateTransition, StateTransitionLike}; -use dpp::system_data_contracts::load_system_data_contract; use dpp::voting::votes::resource_vote::accessors::v0::ResourceVoteGettersV0; use dpp::voting::votes::Vote; use grovedb::{PathQuery, TransactionArg}; From bea7678380bd1ce164dae57688876640890f859d Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Tue, 13 May 2025 05:00:14 +0700 Subject: [PATCH 9/9] fix --- .../drive/group/insert/add_group_action/v0/mod.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs index 084c86d895b..e1561556e4a 100644 --- a/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs +++ b/packages/rs-drive/src/drive/group/insert/add_group_action/v0/mod.rs @@ -380,8 +380,8 @@ impl Drive { BatchMoveApplyType::StatelessBatchMove { in_tree_type: TreeType::NormalTree, tree_type: None, - estimated_key_size: 5, // fixed‑size “info” key - estimated_value_size: 0, // value size unknown / not critical + estimated_key_size: 1, + estimated_value_size: 3, flags_len: 0, } }; @@ -417,15 +417,17 @@ impl Drive { // We then need to then do a delete operation of the action in the active part let delete_apply_type = if estimated_costs_only_with_layer_info.is_none() { - BatchDeleteApplyType::StatefulBatchDelete { - is_known_to_be_subtree_with_sum: Some(NotTree), - } - } else { BatchDeleteApplyType::StatefulBatchDelete { is_known_to_be_subtree_with_sum: Some(MaybeTree::Tree( TreeType::NormalTree, )), } + } else { + BatchDeleteApplyType::StatelessBatchDelete { + in_tree_type: TreeType::NormalTree, + estimated_key_size: 32, + estimated_value_size: 32, + } }; self.batch_delete(