From eb6857b8c41633fba5ac2ce4820686fa21f7185d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 13 Aug 2024 21:18:15 +0700 Subject: [PATCH] fix!: refresh references on identity key re-enable/disable (#2047) Co-authored-by: Quantum Explorer --- .../identity/identity_public_key/purpose.rs | 8 +- .../state_transitions/identity_create/mod.rs | 12 +- .../state_transitions/identity_update/mod.rs | 99 +++++ .../tests/strategy_tests/main.rs | 8 +- .../drive/identity/contract_info/keys/mod.rs | 1 + .../mod.rs | 71 ++++ .../v0/mod.rs | 337 ++++++++++++++++++ .../insert/add_new_identity/v0/mod.rs | 1 + .../v0/mod.rs | 5 +- .../v0/mod.rs | 10 +- .../insert_new_non_unique_key/v0/mod.rs | 4 +- .../insert/insert_new_unique_key/v0/mod.rs | 2 +- packages/rs-drive/src/drive/identity/mod.rs | 7 +- .../methods/disable_identity_keys/mod.rs | 3 + .../methods/disable_identity_keys/v0/mod.rs | 18 + .../src/drive/identity/update/methods/mod.rs | 1 + .../methods/re_enable_identity_keys/mod.rs | 3 + .../methods/re_enable_identity_keys/v0/mod.rs | 12 + .../mod.rs | 53 +++ .../v0/mod.rs | 110 ++++++ .../rs-drive/src/drive/identity/update/mod.rs | 10 +- .../src/util/batch/drive_op_batch/identity.rs | 2 + .../src/version/drive_versions.rs | 2 + .../src/version/mocks/v2_test.rs | 2 + .../src/version/mocks/v3_test.rs | 2 + .../rs-platform-version/src/version/v1.rs | 2 + 26 files changed, 756 insertions(+), 29 deletions(-) create mode 100644 packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/mod.rs create mode 100644 packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/v0/mod.rs create mode 100644 packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/mod.rs create mode 100644 packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/v0/mod.rs diff --git a/packages/rs-dpp/src/identity/identity_public_key/purpose.rs b/packages/rs-dpp/src/identity/identity_public_key/purpose.rs index 17612a9cd2..dbadfa5748 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/purpose.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/purpose.rs @@ -102,12 +102,12 @@ impl std::fmt::Display for Purpose { impl Purpose { /// The full range of purposes - pub fn full_range() -> [Purpose; 4] { - [AUTHENTICATION, ENCRYPTION, DECRYPTION, TRANSFER] + pub fn full_range() -> [Purpose; 5] { + [AUTHENTICATION, ENCRYPTION, DECRYPTION, TRANSFER, VOTING] } /// Just the authentication and withdraw purposes - pub fn authentication_and_transfer() -> [Purpose; 2] { - [AUTHENTICATION, TRANSFER] + pub fn searchable_purposes() -> [Purpose; 3] { + [AUTHENTICATION, TRANSFER, VOTING] } /// Just the encryption and decryption purposes pub fn encryption_decryption() -> [Purpose; 2] { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/mod.rs index 2501af1fe4..1e0d8bf4b0 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/mod.rs @@ -309,7 +309,7 @@ mod tests { assert_eq!(processing_result.valid_count(), 1); - assert_eq!(processing_result.aggregated_fees().processing_fee, 1823240); + assert_eq!(processing_result.aggregated_fees().processing_fee, 1871240); platform .drive @@ -324,7 +324,7 @@ mod tests { .expect("expected to get identity balance") .expect("expected there to be an identity balance for this identity"); - assert_eq!(identity_balance, 99916906760); + assert_eq!(identity_balance, 99913915760); } #[test] @@ -531,7 +531,7 @@ mod tests { assert_eq!(processing_result.valid_count(), 1); - assert_eq!(processing_result.aggregated_fees().processing_fee, 2098900); + assert_eq!(processing_result.aggregated_fees().processing_fee, 2146900); platform .drive @@ -546,7 +546,7 @@ mod tests { .expect("expected to get identity balance") .expect("expected there to be an identity balance for this identity"); - assert_eq!(identity_balance, 99912301400); // The identity balance is smaller than if there hadn't been any issue + assert_eq!(identity_balance, 99909310400); // The identity balance is smaller than if there hadn't been any issue } #[test] @@ -1235,7 +1235,7 @@ mod tests { assert_eq!(processing_result.valid_count(), 1); - assert_eq!(processing_result.aggregated_fees().processing_fee, 2098900); + assert_eq!(processing_result.aggregated_fees().processing_fee, 2146900); platform .drive @@ -1250,6 +1250,6 @@ mod tests { .expect("expected to get identity balance") .expect("expected there to be an identity balance for this identity"); - assert_eq!(identity_balance, 99912301400); // The identity balance is smaller than if there hadn't been any issue + assert_eq!(identity_balance, 99909310400); // The identity balance is smaller than if there hadn't been any issue } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs index 9b8cd341e8..a2f898fa28 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs @@ -109,3 +109,102 @@ impl StateTransitionStateValidationV0 for IdentityUpdateTransition { } } } + +#[cfg(test)] +mod tests { + use crate::config::{PlatformConfig, PlatformTestConfig}; + use crate::execution::validation::state_transition::tests::setup_identity_return_master_key; + use crate::test::helpers::setup::TestPlatformBuilder; + use dpp::block::block_info::BlockInfo; + use dpp::dash_to_credits; + use dpp::identity::accessors::IdentityGettersV0; + use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; + use dpp::identity::signer::Signer; + use dpp::serialization::{PlatformSerializable, Signable}; + use dpp::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0; + use dpp::state_transition::identity_update_transition::IdentityUpdateTransition; + use dpp::state_transition::StateTransition; + use platform_version::version::PlatformVersion; + + #[test] + fn test_identity_update_that_disables_a_key() { + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let platform_version = PlatformVersion::latest(); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .build_with_mock_rpc() + .set_genesis_state(); + + let (identity, signer, key) = + setup_identity_return_master_key(&mut platform, 958, dash_to_credits!(0.1)); + + let platform_state = platform.state.load(); + + let update_transition: IdentityUpdateTransition = IdentityUpdateTransitionV0 { + identity_id: identity.id(), + revision: 1, + nonce: 1, + add_public_keys: vec![], + disable_public_keys: vec![1], + user_fee_increase: 0, + signature_public_key_id: key.id(), + signature: Default::default(), + } + .into(); + + let mut update_transition: StateTransition = update_transition.into(); + + let data = update_transition + .signable_bytes() + .expect("expected signable bytes"); + update_transition.set_signature( + signer + .sign(&key, data.as_slice()) + .expect("expected to sign"), + ); + + let update_transition_bytes = update_transition + .serialize_to_bytes() + .expect("expected to serialize"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![update_transition_bytes.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + true, + None, + ) + .expect("expected to process state transition"); + + assert_eq!(processing_result.valid_count(), 1); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit"); + + let issues = platform + .drive + .grove + .visualize_verify_grovedb(true, &platform_version.drive.grove_version) + .expect("expected to have no issues"); + + assert_eq!(issues.len(), 0); + } +} diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index bcbb6f027f..e592c5d5e7 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -527,7 +527,7 @@ mod tests { .expect("expected to fetch balances") .expect("expected to have an identity to get balance from"); - assert_eq!(balance, 99867003200) + assert_eq!(balance, 99864012200) } #[test] @@ -1194,7 +1194,7 @@ mod tests { .unwrap() .unwrap() ), - "4f4fc212d1d0c631b985a29544a24fbe6f7ffdd9e8d69a69d2da173dea567945".to_string() + "d12cc15405f4a810c239539f06fe6eee9f2b2d1ad49055a5ca55882b5842baa4".to_string() ) } @@ -1915,7 +1915,7 @@ mod tests { .unwrap() .unwrap() ), - "cdb5dc16b1e774c0fc777f1b7ffa16b652ba5a80ad8329d75accf30b19f97dd8".to_string() + "48cfe777a90adbe975bfab264e7a2a0951c93386ef81f17bfe6640ad1251feda".to_string() ) } @@ -2050,7 +2050,7 @@ mod tests { .unwrap() .unwrap() ), - "f7422a9297c1c2b903dac3873f31604f6268e69498e06c658c2948093efb4368".to_string() + "3319167d723776fdb8dab32dd9d360d098a47e7f496b7b4c24419b4edbac5f77".to_string() ) } diff --git a/packages/rs-drive/src/drive/identity/contract_info/keys/mod.rs b/packages/rs-drive/src/drive/identity/contract_info/keys/mod.rs index 387c2ab02e..f005f110f6 100644 --- a/packages/rs-drive/src/drive/identity/contract_info/keys/mod.rs +++ b/packages/rs-drive/src/drive/identity/contract_info/keys/mod.rs @@ -14,6 +14,7 @@ use platform_version::version::PlatformVersion; use std::collections::BTreeMap; mod add_potential_contract_info_for_contract_bounded_key; +mod refresh_potential_contract_info_key_references; pub enum IdentityDataContractKeyApplyInfo { /// The root_id is either a contract id or an owner id diff --git a/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/mod.rs b/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/mod.rs new file mode 100644 index 0000000000..1ca7c91d3b --- /dev/null +++ b/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/mod.rs @@ -0,0 +1,71 @@ +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use dpp::block::epoch::Epoch; +use dpp::identity::IdentityPublicKey; +use grovedb::batch::KeyInfoPath; +use grovedb::{EstimatedLayerInformation, TransactionArg}; +use platform_version::version::PlatformVersion; +use std::collections::HashMap; + +mod v0; + +impl Drive { + /// Adds potential contract information for a contract-bounded key. + /// + /// This function considers the contract bounds associated with an identity key and forms the operations needed to process the contract information. + /// + /// # Arguments + /// + /// * `identity_id` - An array of bytes representing the identity id. + /// * `identity_key` - A reference to the `IdentityPublicKey` associated with the contract. + /// * `epoch` - The current epoch. + /// * `estimated_costs_only_with_layer_info` - A mutable reference to an optional `HashMap` that may contain estimated layer information. + /// * `transaction` - The transaction arguments. + /// * `drive_operations` - A mutable reference to a vector of `LowLevelDriveOperation` objects. + /// * `platform_version` - A reference to the platform version information. + /// + /// # Returns + /// + /// * `Result<(), Error>` - If successful, returns unit (`()`). If an error occurs during the operation, returns an `Error`. + /// + /// # Errors + /// + /// This function may return an `Error` if the operation creation process fails or if the platform version does not match any of the implemented method versions. + pub(crate) fn refresh_potential_contract_info_key_references( + &self, + identity_id: [u8; 32], + identity_key: &IdentityPublicKey, + epoch: &Epoch, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + match platform_version + .drive + .methods + .identity + .contract_info + .refresh_potential_contract_info_key_references + { + 0 => self.refresh_potential_contract_info_key_references_v0( + identity_id, + identity_key, + epoch, + estimated_costs_only_with_layer_info, + transaction, + drive_operations, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "refresh_potential_contract_info_key_references".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/v0/mod.rs b/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/v0/mod.rs new file mode 100644 index 0000000000..8183b3ae9e --- /dev/null +++ b/packages/rs-drive/src/drive/identity/contract_info/keys/refresh_potential_contract_info_key_references/v0/mod.rs @@ -0,0 +1,337 @@ +use crate::drive::identity::contract_info::keys::IdentityDataContractKeyApplyInfo; +use crate::drive::identity::{ + identity_contract_info_group_keys_path_vec, identity_contract_info_group_path_key_purpose_vec, + identity_key_location_within_identity_vec, +}; +use crate::drive::Drive; +use crate::error::contract::DataContractError; +use crate::error::identity::IdentityError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use dpp::block::epoch::Epoch; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::config::v0::DataContractConfigGettersV0; +use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; +use dpp::data_contract::storage_requirements::keys_for_document_type::StorageKeyRequirements; +use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dpp::identity::{IdentityPublicKey, Purpose}; +use dpp::version::PlatformVersion; +use grovedb::batch::KeyInfoPath; +use grovedb::reference_path::ReferencePathType::{SiblingReference, UpstreamRootHeightReference}; +use grovedb::{Element, EstimatedLayerInformation, TransactionArg}; +use grovedb_costs::OperationCost; +use integer_encoding::VarInt; +use std::collections::HashMap; + +impl Drive { + #[inline(always)] + pub(in crate::drive::identity::contract_info) fn refresh_potential_contract_info_key_references_v0( + &self, + identity_id: [u8; 32], + identity_key: &IdentityPublicKey, + epoch: &Epoch, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + if let Some(contract_bounds) = &identity_key.contract_bounds() { + // We need to get the contract + let contract_apply_info = IdentityDataContractKeyApplyInfo::new_from_single_key( + identity_key.id(), + identity_key.purpose(), + contract_bounds, + self, + epoch, + transaction, + drive_operations, + platform_version, + )?; + self.refresh_contract_info_operations_v0( + identity_id, + epoch, + vec![contract_apply_info], + estimated_costs_only_with_layer_info, + transaction, + drive_operations, + platform_version, + )?; + } + Ok(()) + } + + /// Refreshes keys for the contract info + fn refresh_contract_info_operations_v0( + &self, + identity_id: [u8; 32], + epoch: &Epoch, + contract_infos: Vec, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + if let Some(estimated_costs_only_with_layer_info) = estimated_costs_only_with_layer_info { + Self::add_estimation_costs_for_contract_info( + &identity_id, + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + for contract_info in contract_infos.into_iter() { + let root_id = contract_info.root_id(); + + let contract = if estimated_costs_only_with_layer_info.is_none() { + // we should start by fetching the contract + let (fee, contract) = self.get_contract_with_fetch_info_and_fee( + root_id, + Some(epoch), + true, + transaction, + platform_version, + )?; + + let fee = fee.ok_or(Error::Identity( + IdentityError::IdentityKeyDataContractNotFound, + ))?; + let contract = contract.ok_or(Error::Identity( + IdentityError::IdentityKeyDataContractNotFound, + ))?; + drive_operations.push(LowLevelDriveOperation::PreCalculatedFeeResult(fee)); + Some(contract) + } else { + drive_operations.push(LowLevelDriveOperation::CalculatedCostOperation( + OperationCost { + seek_count: 1, + storage_cost: Default::default(), + storage_loaded_bytes: 100, + hash_node_calls: 0, + }, + )); + None + }; + + let (document_keys, contract_or_family_keys) = contract_info.keys(); + + for (key_id, purpose) in contract_or_family_keys { + if let Some(estimated_costs_only_with_layer_info) = + estimated_costs_only_with_layer_info + { + Self::add_estimation_costs_for_contract_info_group_key_purpose( + &identity_id, + &root_id, + purpose, + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + // we need to add a reference to the key + let key_id_bytes = key_id.encode_var_vec(); + let key_reference = + identity_key_location_within_identity_vec(key_id_bytes.as_slice()); + + let reference_type_path = UpstreamRootHeightReference(2, key_reference); + + // at this point we want to know if the contract is single key or multiple key + let storage_key_requirements = contract + .as_ref() + .map(|contract| match purpose { + Purpose::ENCRYPTION => { + let encryption_storage_key_requirements = contract + .contract + .config() + .requires_identity_encryption_bounded_key() + .ok_or(Error::DataContract( + DataContractError::KeyBoundsExpectedButNotPresent( + "expected encryption key bounds for encryption", + ), + ))?; + Ok(encryption_storage_key_requirements) + } + Purpose::DECRYPTION => { + let decryption_storage_key_requirements = contract + .contract + .config() + .requires_identity_decryption_bounded_key() + .ok_or(Error::DataContract( + DataContractError::KeyBoundsExpectedButNotPresent( + "expected encryption key bounds for decryption", + ), + ))?; + Ok(decryption_storage_key_requirements) + } + _ => Err(Error::Identity(IdentityError::IdentityKeyBoundsError( + "purpose not available for key bounds", + ))), + }) + .transpose()? + .unwrap_or(StorageKeyRequirements::MultipleReferenceToLatest); + + // if we are multiple we refresh the key under the key bytes, otherwise it is under 0 + + let key = if storage_key_requirements == StorageKeyRequirements::Unique { + vec![] + } else { + key_id_bytes.clone() + }; + + self.batch_refresh_reference( + identity_contract_info_group_path_key_purpose_vec( + &identity_id, + &root_id, + purpose, + ), + key, + Element::Reference(reference_type_path, Some(1), None), + true, + drive_operations, + &platform_version.drive, + )?; + + if storage_key_requirements == StorageKeyRequirements::MultipleReferenceToLatest { + // we also refresh the sibling reference, so we can query the current key + + let sibling_ref_type_path = SiblingReference(key_id_bytes); + + self.batch_refresh_reference( + identity_contract_info_group_keys_path_vec(&identity_id, &root_id), + vec![], + Element::Reference(sibling_ref_type_path, Some(2), None), + true, + drive_operations, + &platform_version.drive, + )?; + } + } + + for (document_type_name, document_key_ids) in document_keys { + // The path is the concatenation of the contract_id and the document type name + let mut contract_id_bytes_with_document_type_name = root_id.to_vec(); + contract_id_bytes_with_document_type_name.extend(document_type_name.as_bytes()); + + if let Some(estimated_costs_only_with_layer_info) = + estimated_costs_only_with_layer_info + { + Self::add_estimation_costs_for_contract_info_group( + &identity_id, + &contract_id_bytes_with_document_type_name, + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + + Self::add_estimation_costs_for_contract_info_group_keys( + &identity_id, + &contract_id_bytes_with_document_type_name, + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + for (key_id, purpose) in document_key_ids { + if let Some(estimated_costs_only_with_layer_info) = + estimated_costs_only_with_layer_info + { + Self::add_estimation_costs_for_contract_info_group_key_purpose( + &identity_id, + &contract_id_bytes_with_document_type_name, + purpose, + estimated_costs_only_with_layer_info, + &platform_version.drive, + )?; + } + + // we need to add a reference to the key + let key_id_bytes = key_id.encode_var_vec(); + let key_reference = + identity_key_location_within_identity_vec(key_id_bytes.as_slice()); + + let reference = UpstreamRootHeightReference(2, key_reference); + + // at this point we want to know if the contract is single key or multiple key + let storage_key_requirements = contract + .as_ref() + .map(|contract| match purpose { + Purpose::ENCRYPTION => { + let document_type = contract + .contract + .document_type_for_name(document_type_name.as_str())?; + let encryption_storage_key_requirements = document_type + .requires_identity_encryption_bounded_key() + .ok_or(Error::DataContract( + DataContractError::KeyBoundsExpectedButNotPresent( + "expected encryption key bounds in document type", + ), + ))?; + Ok(encryption_storage_key_requirements) + } + Purpose::DECRYPTION => { + let document_type = contract + .contract + .document_type_for_name(document_type_name.as_str())?; + let decryption_storage_key_requirements = document_type + .requires_identity_decryption_bounded_key() + .ok_or(Error::DataContract( + DataContractError::KeyBoundsExpectedButNotPresent( + "expected encryption key bounds in document type", + ), + ))?; + Ok(decryption_storage_key_requirements) + } + _ => Err(Error::Identity(IdentityError::IdentityKeyBoundsError( + "purpose not available for key bounds", + ))), + }) + .transpose()? + .unwrap_or(StorageKeyRequirements::MultipleReferenceToLatest); + + let key = if storage_key_requirements == StorageKeyRequirements::Unique { + vec![] + } else { + key_id_bytes.clone() + }; + + self.batch_refresh_reference( + identity_contract_info_group_path_key_purpose_vec( + &identity_id, + &contract_id_bytes_with_document_type_name, + purpose, + ), + key, + Element::Reference(reference, Some(1), None), + true, + drive_operations, + &platform_version.drive, + )?; + + if storage_key_requirements == StorageKeyRequirements::MultipleReferenceToLatest + { + // we also need to refresh the sibling reference, so we can query the current key + + let sibling_ref_type_path = SiblingReference(key_id_bytes); + + self.batch_refresh_reference( + identity_contract_info_group_path_key_purpose_vec( + &identity_id, + &contract_id_bytes_with_document_type_name, + purpose, + ), + vec![], + Element::Reference(sibling_ref_type_path, Some(2), None), + true, + drive_operations, + &platform_version.drive, + )?; + } + } + } + } + + Ok(()) + } +} diff --git a/packages/rs-drive/src/drive/identity/insert/add_new_identity/v0/mod.rs b/packages/rs-drive/src/drive/identity/insert/add_new_identity/v0/mod.rs index 1665f79e9a..6b6dfbf6d0 100644 --- a/packages/rs-drive/src/drive/identity/insert/add_new_identity/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/insert/add_new_identity/v0/mod.rs @@ -189,6 +189,7 @@ impl Drive { .iter() .map(|identity_public_key| identity_public_key.id()) .collect(), + &block_info.epoch, estimated_costs_only_with_layer_info, transaction, platform_version, diff --git a/packages/rs-drive/src/drive/identity/key/insert/create_new_identity_key_query_trees/v0/mod.rs b/packages/rs-drive/src/drive/identity/key/insert/create_new_identity_key_query_trees/v0/mod.rs index 041ae38b51..0e5c38f85c 100644 --- a/packages/rs-drive/src/drive/identity/key/insert/create_new_identity_key_query_trees/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/key/insert/create_new_identity_key_query_trees/v0/mod.rs @@ -33,8 +33,9 @@ impl Drive { )?; } - // There are 4 Purposes: Authentication, Encryption, Decryption, Transfer - for purpose in Purpose::authentication_and_transfer() { + // There are 5 Purposes: Authentication, Encryption, Decryption, Transfer and Voting + // Searchable purposes are Authentication, Transfer and Voting + for purpose in Purpose::searchable_purposes() { self.batch_insert_empty_tree( identity_query_key_tree, DriveKeyInfo::Key(vec![purpose as u8]), diff --git a/packages/rs-drive/src/drive/identity/key/insert/insert_key_searchable_references/v0/mod.rs b/packages/rs-drive/src/drive/identity/key/insert/insert_key_searchable_references/v0/mod.rs index 07d076b007..8ae39f8b34 100644 --- a/packages/rs-drive/src/drive/identity/key/insert/insert_key_searchable_references/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/key/insert/insert_key_searchable_references/v0/mod.rs @@ -1,7 +1,8 @@ use crate::drive::identity::{ identity_key_location_within_identity_vec, identity_query_keys_for_authentication_full_tree_path, - identity_query_keys_for_transfer_full_tree_path, identity_query_keys_purpose_tree_path, + identity_query_keys_for_direct_searchable_reference_full_tree_path, + identity_query_keys_purpose_tree_path, }; use crate::drive::Drive; use crate::error::Error; @@ -116,10 +117,13 @@ impl Drive { drive_version, ) } - Purpose::TRANSFER => { + Purpose::TRANSFER | Purpose::VOTING => { // Now let's set the reference let reference_path = - identity_query_keys_for_transfer_full_tree_path(identity_id.as_slice()); + identity_query_keys_for_direct_searchable_reference_full_tree_path( + purpose, + identity_id.as_slice(), + ); let key_reference = identity_key_location_within_identity_vec(key_id_bytes); self.batch_insert( PathFixedSizeKeyRefElement(( diff --git a/packages/rs-drive/src/drive/identity/key/insert/insert_new_non_unique_key/v0/mod.rs b/packages/rs-drive/src/drive/identity/key/insert/insert_new_non_unique_key/v0/mod.rs index 12ec7f22c7..9046e151ec 100644 --- a/packages/rs-drive/src/drive/identity/key/insert/insert_new_non_unique_key/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/key/insert/insert_new_non_unique_key/v0/mod.rs @@ -12,7 +12,7 @@ use platform_version::version::PlatformVersion; use std::collections::HashMap; impl Drive { - /// Insert a new non unique key into an identity operations + /// Insert a new non-unique key into an identity operations pub(super) fn insert_new_non_unique_key_operations_v0( &self, identity_id: [u8; 32], @@ -63,7 +63,7 @@ impl Drive { if with_searchable_inner_references && matches!( identity_key.purpose(), - Purpose::AUTHENTICATION | Purpose::TRANSFER + Purpose::AUTHENTICATION | Purpose::TRANSFER | Purpose::VOTING ) { self.insert_key_searchable_references_operations( diff --git a/packages/rs-drive/src/drive/identity/key/insert/insert_new_unique_key/v0/mod.rs b/packages/rs-drive/src/drive/identity/key/insert/insert_new_unique_key/v0/mod.rs index 5f2f2c906e..ad12a17b3c 100644 --- a/packages/rs-drive/src/drive/identity/key/insert/insert_new_unique_key/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/key/insert/insert_new_unique_key/v0/mod.rs @@ -59,7 +59,7 @@ impl Drive { if with_searchable_inner_references && matches!( identity_key.purpose(), - Purpose::AUTHENTICATION | Purpose::TRANSFER + Purpose::AUTHENTICATION | Purpose::TRANSFER | Purpose::VOTING ) { self.insert_key_searchable_references_operations( diff --git a/packages/rs-drive/src/drive/identity/mod.rs b/packages/rs-drive/src/drive/identity/mod.rs index afd1b3461b..65928b5ecb 100644 --- a/packages/rs-drive/src/drive/identity/mod.rs +++ b/packages/rs-drive/src/drive/identity/mod.rs @@ -276,12 +276,15 @@ pub(crate) fn identity_query_keys_security_level_tree_path_vec( /// identity query keys full tree path #[cfg(feature = "server")] /// Identity query keys full tree path -pub(crate) fn identity_query_keys_for_transfer_full_tree_path(identity_id: &[u8]) -> [&[u8]; 4] { +pub(crate) fn identity_query_keys_for_direct_searchable_reference_full_tree_path( + purpose: Purpose, + identity_id: &[u8], +) -> [&[u8]; 4] { [ Into::<&[u8; 1]>::into(RootTree::Identities), identity_id, Into::<&[u8; 1]>::into(IdentityRootStructure::IdentityTreeKeyReferences), - Into::<&[u8; 1]>::into(Purpose::TRANSFER), + Into::<&[u8; 1]>::into(purpose), ] } diff --git a/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/mod.rs index 127c020f27..9305f6c354 100644 --- a/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/mod.rs @@ -13,6 +13,7 @@ use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; +use dpp::block::epoch::Epoch; use std::collections::HashMap; impl Drive { @@ -84,6 +85,7 @@ impl Drive { identity_id: [u8; 32], key_ids: Vec, disable_at: TimestampMillis, + epoch: &Epoch, estimated_costs_only_with_layer_info: &mut Option< HashMap, >, @@ -101,6 +103,7 @@ impl Drive { identity_id, key_ids, disable_at, + epoch, estimated_costs_only_with_layer_info, transaction, platform_version, diff --git a/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/v0/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/v0/mod.rs index 4f4052dac9..f1505c71d3 100644 --- a/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/methods/disable_identity_keys/v0/mod.rs @@ -16,6 +16,7 @@ use dpp::identity::identity_public_key::accessors::v0::{ use dpp::identity::{IdentityPublicKey, KeyID}; use dpp::prelude::TimestampMillis; +use dpp::block::epoch::Epoch; use dpp::version::PlatformVersion; use dpp::ProtocolError; use grovedb::{EstimatedLayerInformation, TransactionArg}; @@ -44,6 +45,7 @@ impl Drive { identity_id, keys_ids, disable_at, + &block_info.epoch, &mut estimated_costs_only_with_layer_info, transaction, platform_version, @@ -99,6 +101,7 @@ impl Drive { identity_id: [u8; 32], key_ids: Vec, disable_at: TimestampMillis, + epoch: &Epoch, estimated_costs_only_with_layer_info: &mut Option< HashMap, >, @@ -120,6 +123,11 @@ impl Drive { estimated_costs_only_with_layer_info, drive_version, )?; + Self::add_estimation_costs_for_root_key_reference_tree( + identity_id, + estimated_costs_only_with_layer_info, + drive_version, + )?; key_ids .into_iter() .map(|key_id| { @@ -167,6 +175,16 @@ impl Drive { &mut drive_operations, drive_version, )?; + + self.refresh_identity_key_reference_operations( + identity_id, + &key, + epoch, + estimated_costs_only_with_layer_info, + transaction, + &mut drive_operations, + platform_version, + )? } Ok(drive_operations) diff --git a/packages/rs-drive/src/drive/identity/update/methods/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/mod.rs index 4de31c83ff..a014568111 100644 --- a/packages/rs-drive/src/drive/identity/update/methods/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/methods/mod.rs @@ -7,5 +7,6 @@ mod apply_balance_change_from_fee_to_identity; mod disable_identity_keys; pub(crate) mod merge_identity_nonce; mod re_enable_identity_keys; +mod refresh_identity_key_reference_operations; mod remove_from_identity_balance; mod update_identity_revision; diff --git a/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/mod.rs index ac2bba2f82..dd89d99f60 100644 --- a/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/mod.rs @@ -10,6 +10,7 @@ use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; +use dpp::block::epoch::Epoch; use std::collections::HashMap; impl Drive { @@ -35,6 +36,7 @@ impl Drive { &self, identity_id: [u8; 32], key_ids: Vec, + epoch: &Epoch, estimated_costs_only_with_layer_info: &mut Option< HashMap, >, @@ -51,6 +53,7 @@ impl Drive { 0 => self.re_enable_identity_keys_operations_v0( identity_id, key_ids, + epoch, estimated_costs_only_with_layer_info, transaction, platform_version, diff --git a/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/v0/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/v0/mod.rs index 9d81926d17..1434691be2 100644 --- a/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/methods/re_enable_identity_keys/v0/mod.rs @@ -10,6 +10,7 @@ use dpp::identity::identity_public_key::accessors::v0::{ }; use dpp::identity::{IdentityPublicKey, KeyID}; +use dpp::block::epoch::Epoch; use dpp::version::PlatformVersion; use dpp::ProtocolError; use grovedb::batch::KeyInfoPath; @@ -45,6 +46,7 @@ impl Drive { &self, identity_id: [u8; 32], key_ids: Vec, + epoch: &Epoch, estimated_costs_only_with_layer_info: &mut Option< HashMap, >, @@ -112,6 +114,16 @@ impl Drive { &mut drive_operations, drive_version, )?; + + self.refresh_identity_key_reference_operations( + identity_id, + &key, + epoch, + estimated_costs_only_with_layer_info, + transaction, + &mut drive_operations, + platform_version, + )? } Ok(drive_operations) diff --git a/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/mod.rs new file mode 100644 index 0000000000..cf10dce236 --- /dev/null +++ b/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/mod.rs @@ -0,0 +1,53 @@ +mod v0; + +use crate::drive::Drive; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; + +use dpp::version::PlatformVersion; +use grovedb::batch::KeyInfoPath; +use grovedb::{EstimatedLayerInformation, TransactionArg}; + +use dpp::block::epoch::Epoch; +use dpp::identity::IdentityPublicKey; +use std::collections::HashMap; + +impl Drive { + /// Refreshes the identity key reference + pub fn refresh_identity_key_reference_operations( + &self, + identity_id: [u8; 32], + key: &IdentityPublicKey, + epoch: &Epoch, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + match platform_version + .drive + .methods + .identity + .update + .refresh_identity_key_reference_operations + { + 0 => self.refresh_identity_key_reference_operations_v0( + identity_id, + key, + epoch, + estimated_costs_only_with_layer_info, + transaction, + drive_operations, + platform_version, + ), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "refresh_identity_key_reference_v0".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/v0/mod.rs b/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/v0/mod.rs new file mode 100644 index 0000000000..9c1a800722 --- /dev/null +++ b/packages/rs-drive/src/drive/identity/update/methods/refresh_identity_key_reference_operations/v0/mod.rs @@ -0,0 +1,110 @@ +use crate::drive::identity::{ + identity_key_location_within_identity_vec, identity_query_keys_purpose_tree_path_vec, + identity_query_keys_security_level_tree_path_vec, +}; +use crate::drive::Drive; +use crate::error::Error; +use crate::fees::op::LowLevelDriveOperation; +use dpp::block::epoch::Epoch; +use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dpp::identity::{IdentityPublicKey, Purpose}; +use grovedb::batch::KeyInfoPath; +use grovedb::reference_path::ReferencePathType; +use grovedb::{Element, EstimatedLayerInformation, TransactionArg}; +use integer_encoding::VarInt; +use platform_version::version::PlatformVersion; +use std::collections::HashMap; + +impl Drive { + /// Refreshes identity key reference operations. + pub fn refresh_identity_key_reference_operations_v0( + &self, + identity_id: [u8; 32], + key: &IdentityPublicKey, + epoch: &Epoch, + estimated_costs_only_with_layer_info: &mut Option< + HashMap, + >, + transaction: TransactionArg, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + // At this point, we need to refresh reference to that Identity key that was just updated (disable is an update) + + let key_id_bytes = key.id().encode_var_vec(); + + let key_reference = identity_key_location_within_identity_vec(&key_id_bytes); + + let identity_key_reference = Element::new_reference_with_flags( + ReferencePathType::UpstreamRootHeightReference(2, key_reference), + None, + ); + + let trust_refresh_reference = true; // todo: check if this needs to be false + + let purpose = key.purpose(); + let security_level = key.security_level(); + + if let Some(estimated_costs_only_with_layer_info) = estimated_costs_only_with_layer_info { + Self::add_estimation_costs_for_purpose_in_key_reference_tree( + identity_id, + estimated_costs_only_with_layer_info, + key.purpose(), + &platform_version.drive, + )?; + + if matches!(purpose, Purpose::AUTHENTICATION) { + Self::add_estimation_costs_for_authentication_keys_security_level_in_key_reference_tree( + identity_id, + estimated_costs_only_with_layer_info, + key.security_level(), + &platform_version.drive, + )?; + } + } + + let key_path_for_refresh = match purpose { + Purpose::AUTHENTICATION => { + // Now let's set the reference + Some(identity_query_keys_security_level_tree_path_vec( + identity_id.as_slice(), + security_level, + )) + } + Purpose::TRANSFER | Purpose::VOTING => { + // Now let's set the reference + Some(identity_query_keys_purpose_tree_path_vec( + identity_id.as_slice(), + purpose, + )) + } + _ => None, + }; + + if let Some(key_path) = key_path_for_refresh { + self.batch_refresh_reference( + key_path, + key_id_bytes.to_vec(), + identity_key_reference.clone(), + trust_refresh_reference, + drive_operations, + &platform_version.drive, + )?; + } + + if key.contract_bounds().is_some() { + // if there are contract bounds we need to insert them + self.refresh_potential_contract_info_key_references( + identity_id, + &key, + epoch, + estimated_costs_only_with_layer_info, + transaction, + drive_operations, + platform_version, + )?; + } + + Ok(()) + } +} diff --git a/packages/rs-drive/src/drive/identity/update/mod.rs b/packages/rs-drive/src/drive/identity/update/mod.rs index e7ebcc7d2b..b28d7f3134 100644 --- a/packages/rs-drive/src/drive/identity/update/mod.rs +++ b/packages/rs-drive/src/drive/identity/update/mod.rs @@ -61,7 +61,7 @@ mod tests { fee_result, FeeResult { storage_fee: 14202000, - processing_fee: 1097520, + processing_fee: 1098260, ..Default::default() } ); @@ -121,7 +121,7 @@ mod tests { fee_result, FeeResult { storage_fee: 347382000, - processing_fee: 6818480, + processing_fee: 6819220, ..Default::default() } ); @@ -255,7 +255,7 @@ mod tests { fee_result, FeeResult { storage_fee: 513000, - processing_fee: 499220, + processing_fee: 869380, ..Default::default() } ); @@ -320,7 +320,7 @@ mod tests { fee_result, FeeResult { storage_fee: 486000, - processing_fee: 2429120, + processing_fee: 3216860, ..Default::default() } ); @@ -374,7 +374,7 @@ mod tests { ) .expect("should get the cost of the disabling a few keys"); - assert_eq!(expected_fee_result.storage_fee, fee_result.storage_fee,); + assert_eq!(expected_fee_result.storage_fee, fee_result.storage_fee); } } diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/identity.rs b/packages/rs-drive/src/util/batch/drive_op_batch/identity.rs index 22d407f79c..e3ed135a51 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/identity.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/identity.rs @@ -172,6 +172,7 @@ impl DriveLowLevelOperationConverter for IdentityOperationType { identity_id, keys_ids, block_info.time_ms, + &block_info.epoch, estimated_costs_only_with_layer_info, transaction, platform_version, @@ -182,6 +183,7 @@ impl DriveLowLevelOperationConverter for IdentityOperationType { } => drive.re_enable_identity_keys_operations( identity_id, keys_ids, + &block_info.epoch, estimated_costs_only_with_layer_info, transaction, platform_version, diff --git a/packages/rs-platform-version/src/version/drive_versions.rs b/packages/rs-platform-version/src/version/drive_versions.rs index 25daa65f6a..1cfcf764e1 100644 --- a/packages/rs-platform-version/src/version/drive_versions.rs +++ b/packages/rs-platform-version/src/version/drive_versions.rs @@ -583,6 +583,7 @@ pub struct DriveIdentityWithdrawalTransactionQueueMethodVersions { #[derive(Clone, Debug, Default)] pub struct DriveIdentityContractInfoMethodVersions { pub add_potential_contract_info_for_contract_bounded_key: FeatureVersion, + pub refresh_potential_contract_info_key_references: FeatureVersion, pub merge_identity_contract_nonce: FeatureVersion, } @@ -727,6 +728,7 @@ pub struct DriveIdentityUpdateMethodVersions { pub add_to_previous_balance: FeatureVersion, pub apply_balance_change_from_fee_to_identity: FeatureVersion, pub remove_from_identity_balance: FeatureVersion, + pub refresh_identity_key_reference_operations: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/mocks/v2_test.rs b/packages/rs-platform-version/src/version/mocks/v2_test.rs index bfc55b570a..5cc03cec93 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -433,12 +433,14 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { add_to_previous_balance: 0, apply_balance_change_from_fee_to_identity: 0, remove_from_identity_balance: 0, + refresh_identity_key_reference_operations: 0, }, insert: DriveIdentityInsertMethodVersions { add_new_identity: 0, }, contract_info: DriveIdentityContractInfoMethodVersions { add_potential_contract_info_for_contract_bounded_key: 0, + refresh_potential_contract_info_key_references: 0, merge_identity_contract_nonce: 0, }, cost_estimation: DriveIdentityCostEstimationMethodVersions { diff --git a/packages/rs-platform-version/src/version/mocks/v3_test.rs b/packages/rs-platform-version/src/version/mocks/v3_test.rs index 73610024e3..19209abad6 100644 --- a/packages/rs-platform-version/src/version/mocks/v3_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v3_test.rs @@ -462,12 +462,14 @@ pub const TEST_PLATFORM_V3: PlatformVersion = PlatformVersion { add_to_previous_balance: 0, apply_balance_change_from_fee_to_identity: 0, remove_from_identity_balance: 0, + refresh_identity_key_reference_operations: 0, }, insert: DriveIdentityInsertMethodVersions { add_new_identity: 0, }, contract_info: DriveIdentityContractInfoMethodVersions { add_potential_contract_info_for_contract_bounded_key: 0, + refresh_potential_contract_info_key_references: 0, merge_identity_contract_nonce: 0, }, cost_estimation: DriveIdentityCostEstimationMethodVersions { diff --git a/packages/rs-platform-version/src/version/v1.rs b/packages/rs-platform-version/src/version/v1.rs index edb0838895..bc99fdcd41 100644 --- a/packages/rs-platform-version/src/version/v1.rs +++ b/packages/rs-platform-version/src/version/v1.rs @@ -432,12 +432,14 @@ pub const PLATFORM_V1: PlatformVersion = PlatformVersion { add_to_previous_balance: 0, apply_balance_change_from_fee_to_identity: 0, remove_from_identity_balance: 0, + refresh_identity_key_reference_operations: 0, }, insert: DriveIdentityInsertMethodVersions { add_new_identity: 0, }, contract_info: DriveIdentityContractInfoMethodVersions { add_potential_contract_info_for_contract_bounded_key: 0, + refresh_potential_contract_info_key_references: 0, merge_identity_contract_nonce: 0, }, cost_estimation: DriveIdentityCostEstimationMethodVersions {