From ffb5da7fdf7358ba7f41280063740ea5165167fc Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 17 Nov 2022 05:55:42 +0700 Subject: [PATCH 1/2] a few fixes for storage flags --- dpp/src/data_contract/extra/drive_api.rs | 11 +++++ dpp/src/data_contract/extra/mod.rs | 9 +++- dpp/src/data_contract/extra/mutability.rs | 5 +++ drive/src/drive/contract/mod.rs | 55 ++++++++++++----------- 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/dpp/src/data_contract/extra/drive_api.rs b/dpp/src/data_contract/extra/drive_api.rs index 43590b1b..0f461a1b 100644 --- a/dpp/src/data_contract/extra/drive_api.rs +++ b/dpp/src/data_contract/extra/drive_api.rs @@ -24,6 +24,9 @@ pub trait DriveContractExt { fn keeps_history(&self) -> bool; fn set_keeps_history(&mut self, value: bool); + fn can_be_deleted(&self) -> bool; + fn set_can_be_deleted(&mut self, can_be_deleted: bool); + fn readonly(&self) -> bool; fn set_readonly(&mut self, is_read_only: bool); @@ -88,6 +91,13 @@ impl DriveContractExt for DataContract { self.config.keeps_history = value } + fn can_be_deleted(&self) -> bool { + self.config.can_be_deleted + } + fn set_can_be_deleted(&mut self, can_be_deleted: bool) { + self.config.can_be_deleted = can_be_deleted; + } + fn readonly(&self) -> bool { self.config.readonly } @@ -464,6 +474,7 @@ mod test { assert!(matches!( deserialized_contract.config, ContractConfig { + can_be_deleted: false, readonly: true, keeps_history: true, documents_mutable_contract_default: false, diff --git a/dpp/src/data_contract/extra/mod.rs b/dpp/src/data_contract/extra/mod.rs index 9a2cafa6..ff246856 100644 --- a/dpp/src/data_contract/extra/mod.rs +++ b/dpp/src/data_contract/extra/mod.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use ciborium::value::Value as CborValue; +use crate::data_contract::extra::mutability::DEFAULT_CONTRACT_CAN_BE_DELETED; use mutability::{ DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY, DEFAULT_CONTRACT_DOCUMENT_MUTABILITY, DEFAULT_CONTRACT_KEEPS_HISTORY, DEFAULT_CONTRACT_MUTABILITY, @@ -39,6 +40,11 @@ pub fn get_mutability( mutability::property::KEEPS_HISTORY, DEFAULT_CONTRACT_KEEPS_HISTORY, )?; + let can_be_deleted: bool = common::bool_for_system_value_from_tree_map( + contract, + mutability::property::CAN_BE_DELETED, + DEFAULT_CONTRACT_CAN_BE_DELETED, + )?; let readonly: bool = common::bool_for_system_value_from_tree_map( contract, mutability::property::READONLY, @@ -57,8 +63,9 @@ pub fn get_mutability( )?; Ok(ContractConfig { - keeps_history, + can_be_deleted, readonly, + keeps_history, documents_keep_history_contract_default, documents_mutable_contract_default, }) diff --git a/dpp/src/data_contract/extra/mutability.rs b/dpp/src/data_contract/extra/mutability.rs index f2611587..2f685ea4 100644 --- a/dpp/src/data_contract/extra/mutability.rs +++ b/dpp/src/data_contract/extra/mutability.rs @@ -1,11 +1,13 @@ use serde::{Deserialize, Serialize}; pub const DEFAULT_CONTRACT_KEEPS_HISTORY: bool = false; +pub const DEFAULT_CONTRACT_CAN_BE_DELETED: bool = false; pub const DEFAULT_CONTRACT_MUTABILITY: bool = true; pub const DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY: bool = false; pub const DEFAULT_CONTRACT_DOCUMENT_MUTABILITY: bool = true; pub mod property { + pub const CAN_BE_DELETED: &str = "canBeDeleted"; pub const READONLY: &str = "readonly"; pub const KEEPS_HISTORY: &str = "keepsHistory"; pub const DOCUMENTS_KEEP_HISTORY_CONTRACT_DEFAULT: &str = "documentsKeepHistoryContractDefault"; @@ -15,6 +17,8 @@ pub mod property { #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "camelCase", default)] pub struct ContractConfig { + /// Can the contract ever be deleted + pub can_be_deleted: bool, /// Is the contract mutable pub readonly: bool, /// Does the contract keep history when the contract itself changes @@ -28,6 +32,7 @@ pub struct ContractConfig { impl std::default::Default for ContractConfig { fn default() -> Self { ContractConfig { + can_be_deleted: DEFAULT_CONTRACT_CAN_BE_DELETED, readonly: !DEFAULT_CONTRACT_MUTABILITY, keeps_history: DEFAULT_CONTRACT_KEEPS_HISTORY, documents_keep_history_contract_default: DEFAULT_CONTRACT_DOCUMENTS_KEEPS_HISTORY, diff --git a/drive/src/drive/contract/mod.rs b/drive/src/drive/contract/mod.rs index 1133cc49..5b1a9149 100644 --- a/drive/src/drive/contract/mod.rs +++ b/drive/src/drive/contract/mod.rs @@ -190,14 +190,18 @@ impl Drive { let contract = ::from_cbor(&contract_cbor, contract_id)?; - let storage_flags = StorageFlags::new_single_epoch( - block_info.epoch.index, - Some(contract.owner_id.to_buffer()), - ); + let storage_flags = if contract.can_be_deleted() || !contract.readonly() { + Some(StorageFlags::new_single_epoch( + block_info.epoch.index, + Some(contract.owner_id.to_buffer()), + )) + } else { + None + }; let contract_element = Element::Item( contract_cbor, - StorageFlags::map_to_some_element_flags(Some(storage_flags).as_ref()), + StorageFlags::map_to_some_element_flags(storage_flags.as_ref()), ); self.insert_contract_element( @@ -347,14 +351,15 @@ impl Drive { let contract_id = contract_id.unwrap_or_else(|| *contract.id().as_bytes()); - let storage_flags = StorageFlags::new_single_epoch( + // Since we can update the contract by definition it already has storage flags + let storage_flags = Some(StorageFlags::new_single_epoch( block_info.epoch.index, Some(contract.owner_id.to_buffer()), - ); + )); let contract_element = Element::Item( contract_cbor, - StorageFlags::map_to_some_element_flags(Some(storage_flags).as_ref()), + StorageFlags::map_to_some_element_flags(storage_flags.as_ref()), ); let original_contract_fetch_info = self @@ -639,25 +644,23 @@ impl Drive { .fetch_contract(contract_id, epoch, transaction) .unwrap_add_cost(&mut cost)?; - // we only need to pay if epoch is set - if epoch.is_some() { - if let Some(contract_fetch_info) = &result { - // Store a contract in cache - cache - .cached_contracts - .insert(Arc::clone(contract_fetch_info), transaction); - - if epoch.is_some() { - let fee = contract_fetch_info.fee.as_ref().ok_or( - Error::Drive(DriveError::CorruptedCodeExecution( - "should be impossible to not have fee on something just fetched with an epoch", - )) - )?; - drive_operations.push(PreCalculatedFeeResult(fee.clone())); - } - } else if epoch.is_some() { - drive_operations.push(CalculatedCostOperation(cost)); + if let Some(contract_fetch_info) = &result { + // Store a contract in cache + cache + .cached_contracts + .insert(Arc::clone(contract_fetch_info), transaction); + + // we only need to pay if epoch is set + if epoch.is_some() { + let fee = contract_fetch_info.fee.as_ref().ok_or( + Error::Drive(DriveError::CorruptedCodeExecution( + "should be impossible to not have fee on something just fetched with an epoch", + )) + )?; + drive_operations.push(PreCalculatedFeeResult(fee.clone())); } + } else if epoch.is_some() { + drive_operations.push(CalculatedCostOperation(cost)); } Ok(result) } From 985c13b845e85595d0cefc2f1c2b7a2bd4426c58 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 17 Nov 2022 06:03:21 +0700 Subject: [PATCH 2/2] a few fixes for storage flags --- drive/src/drive/contract/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drive/src/drive/contract/mod.rs b/drive/src/drive/contract/mod.rs index 5b1a9149..9b27ae79 100644 --- a/drive/src/drive/contract/mod.rs +++ b/drive/src/drive/contract/mod.rs @@ -370,9 +370,15 @@ impl Drive { &mut drive_operations, )? .ok_or(Error::Drive(DriveError::CorruptedCodeExecution( - "Contract should exists", + "contract should exist", )))?; + if original_contract_fetch_info.contract.readonly() { + return Err(Error::Drive(DriveError::UpdatingReadOnlyImmutableContract( + "original contract is readonly", + ))); + } + self.update_contract_element( contract_element, &contract,