diff --git a/aptos-move/aptos-abstract-gas-usage/src/algebra.rs b/aptos-move/aptos-abstract-gas-usage/src/algebra.rs index c07768fec594ab..c16aa2dfcdb5a6 100644 --- a/aptos-move/aptos-abstract-gas-usage/src/algebra.rs +++ b/aptos-move/aptos-abstract-gas-usage/src/algebra.rs @@ -6,7 +6,7 @@ use aptos_gas_algebra::{ }; use aptos_gas_meter::GasAlgebra; use aptos_gas_schedule::VMGasParameters; -use aptos_vm_types::storage::io_pricing::IoPricing; +use aptos_vm_types::storage::{io_pricing::IoPricing, space_pricing::DiskSpacePricing}; use move_binary_format::errors::PartialVMResult; use std::sync::{Arc, Mutex}; @@ -33,6 +33,10 @@ impl GasAlgebra for CalibrationAlgebra { self.base.io_pricing() } + fn disk_space_pricing(&self) -> &dyn DiskSpacePricing { + self.base.disk_space_pricing() + } + fn balance_internal(&self) -> InternalGas { self.base.balance_internal() } diff --git a/aptos-move/aptos-gas-meter/src/algebra.rs b/aptos-move/aptos-gas-meter/src/algebra.rs index c33efe971b041a..4700f7a67f1564 100644 --- a/aptos-move/aptos-gas-meter/src/algebra.rs +++ b/aptos-move/aptos-gas-meter/src/algebra.rs @@ -5,7 +5,9 @@ use crate::traits::GasAlgebra; use aptos_gas_algebra::{Fee, FeePerGasUnit, Gas, GasExpression, Octa}; use aptos_gas_schedule::VMGasParameters; use aptos_logger::error; -use aptos_vm_types::storage::{io_pricing::IoPricing, StorageGasParameters}; +use aptos_vm_types::storage::{ + io_pricing::IoPricing, space_pricing::DiskSpacePricing, StorageGasParameters, +}; use move_binary_format::errors::{PartialVMError, PartialVMResult}; use move_core_types::{ gas_algebra::{InternalGas, InternalGasUnit}, @@ -87,6 +89,10 @@ impl GasAlgebra for StandardGasAlgebra { &self.storage_gas_params.io_pricing } + fn disk_space_pricing(&self) -> &dyn DiskSpacePricing { + self.storage_gas_params.space_pricing.as_ref() + } + fn balance_internal(&self) -> InternalGas { self.balance } diff --git a/aptos-move/aptos-gas-meter/src/meter.rs b/aptos-move/aptos-gas-meter/src/meter.rs index f55e38bf8dfd44..4dbf4f25d0856a 100644 --- a/aptos-move/aptos-gas-meter/src/meter.rs +++ b/aptos-move/aptos-gas-meter/src/meter.rs @@ -4,9 +4,7 @@ use crate::traits::{AptosGasMeter, GasAlgebra}; use aptos_gas_algebra::{Fee, FeePerGasUnit}; use aptos_gas_schedule::gas_params::{instr::*, txn::*}; -use aptos_types::{ - contract_event::ContractEvent, state_store::state_key::StateKey, write_set::WriteOpSize, -}; +use aptos_types::{state_store::state_key::StateKey, write_set::WriteOpSize}; use move_binary_format::{ errors::{Location, PartialVMError, PartialVMResult, VMResult}, file_format::CodeOffset, @@ -495,36 +493,6 @@ where .map_err(|e| e.finish(Location::Undefined)) } - fn storage_fee_for_state_slot(&self, op_size: &WriteOpSize) -> Fee { - self.vm_gas_params().txn.storage_fee_for_slot(op_size) - } - - fn storage_fee_refund_for_state_slot(&self, op_size: &WriteOpSize) -> Fee { - self.vm_gas_params() - .txn - .storage_fee_refund_for_slot(op_size) - } - - fn storage_fee_for_state_bytes(&self, key: &StateKey, op_size: &WriteOpSize) -> Fee { - self.vm_gas_params().txn.storage_fee_for_bytes(key, op_size) - } - - fn storage_fee_per_event(&self, event: &ContractEvent) -> Fee { - self.vm_gas_params().txn.storage_fee_per_event(event) - } - - fn storage_discount_for_events(&self, total_cost: Fee) -> Fee { - self.vm_gas_params() - .txn - .storage_discount_for_events(total_cost) - } - - fn storage_fee_for_transaction_storage(&self, txn_size: NumBytes) -> Fee { - self.vm_gas_params() - .txn - .storage_fee_for_transaction_storage(txn_size) - } - fn charge_intrinsic_gas_for_transaction(&mut self, txn_size: NumBytes) -> VMResult<()> { let excess = txn_size .checked_sub(self.vm_gas_params().txn.large_transaction_cutoff) diff --git a/aptos-move/aptos-gas-meter/src/traits.rs b/aptos-move/aptos-gas-meter/src/traits.rs index 1526f93d0d6387..f12e00a9de6c47 100644 --- a/aptos-move/aptos-gas-meter/src/traits.rs +++ b/aptos-move/aptos-gas-meter/src/traits.rs @@ -4,11 +4,13 @@ use aptos_gas_algebra::{Fee, FeePerGasUnit, Gas, GasExpression, GasScalingFactor, Octa}; use aptos_gas_schedule::VMGasParameters; use aptos_types::{ - contract_event::ContractEvent, state_store::{state_key::StateKey, state_value::StateValueMetadata}, write_set::WriteOpSize, }; -use aptos_vm_types::{change_set::VMChangeSet, storage::io_pricing::IoPricing}; +use aptos_vm_types::{ + change_set::VMChangeSet, + storage::{io_pricing::IoPricing, space_pricing::DiskSpacePricing}, +}; use move_binary_format::errors::{Location, PartialVMResult, VMResult}; use move_core_types::gas_algebra::{InternalGas, InternalGasUnit, NumBytes}; use move_vm_types::gas::GasMeter as MoveGasMeter; @@ -26,6 +28,9 @@ pub trait GasAlgebra { /// Returns the struct containing the storage-specific gas parameters. fn io_pricing(&self) -> &IoPricing; + /// Returns the disk space pricing strategy. + fn disk_space_pricing(&self) -> &dyn DiskSpacePricing; + /// Returns the current balance, measured in internal gas units. fn balance_internal(&self) -> InternalGas; @@ -108,24 +113,6 @@ pub trait AptosGasMeter: MoveGasMeter { /// storage costs. fn charge_io_gas_for_write(&mut self, key: &StateKey, op: &WriteOpSize) -> VMResult<()>; - /// Calculates the storage fee for a state slot allocation. - fn storage_fee_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - /// Calculates the storage fee refund for a state slot deallocation. - fn storage_fee_refund_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - /// Calculates the storage fee for state byte. - fn storage_fee_for_state_bytes(&self, key: &StateKey, op: &WriteOpSize) -> Fee; - - /// Calculates the storage fee for an event. - fn storage_fee_per_event(&self, event: &ContractEvent) -> Fee; - - /// Calculates the discount applied to the event storage fees, based on a free quota. - fn storage_discount_for_events(&self, total_cost: Fee) -> Fee; - - /// Calculates the storage fee for the transaction. - fn storage_fee_for_transaction_storage(&self, txn_size: NumBytes) -> Fee; - /// Charges the storage fees for writes, events & txn storage in a lump sum, minimizing the /// loss of precision. Refundable portion of the charge is recorded on the WriteOp itself, /// due to which mutable references are required on the parameter list wherever proper. @@ -152,13 +139,16 @@ pub trait AptosGasMeter: MoveGasMeter { return Ok(0.into()); } + let pricing = self.disk_space_pricing(); + let params = &self.vm_gas_params().txn; + // Calculate the storage fees. let mut write_fee = Fee::new(0); let mut total_refund = Fee::new(0); for (key, op_size, op_creation_metadata) in change_set.write_set_iter_mut() { - let slot_fee = self.storage_fee_for_state_slot(&op_size); - let refund = self.storage_fee_refund_for_state_slot(&op_size); - let bytes_fee = self.storage_fee_for_state_bytes(key, &op_size); + let slot_fee = pricing.storage_fee_for_state_slot(params, &op_size); + let refund = pricing.storage_fee_refund_for_state_slot(params, &op_size); + let bytes_fee = pricing.storage_fee_for_state_bytes(params, key, &op_size); Self::maybe_record_storage_deposit(op_creation_metadata, slot_fee); total_refund += refund; @@ -170,13 +160,13 @@ pub trait AptosGasMeter: MoveGasMeter { .events() .iter() .fold(Fee::new(0), |acc, (event, _)| { - acc + self.storage_fee_per_event(event) + acc + pricing.storage_fee_per_event(params, event) }); - let event_discount = self.storage_discount_for_events(event_fee); + let event_discount = pricing.storage_discount_for_events(params, event_fee); let event_net_fee = event_fee .checked_sub(event_discount) .expect("discount should always be less than or equal to total amount"); - let txn_fee = self.storage_fee_for_transaction_storage(txn_size); + let txn_fee = pricing.storage_fee_for_transaction_storage(params, txn_size); let fee = write_fee + event_net_fee + txn_fee; self.charge_storage_fee(fee, gas_unit_price) @@ -216,6 +206,11 @@ pub trait AptosGasMeter: MoveGasMeter { self.algebra().io_pricing() } + /// Returns the disk space pricing strategy. + fn disk_space_pricing(&self) -> &dyn DiskSpacePricing { + self.algebra().disk_space_pricing() + } + /// Returns the remaining balance, measured in (external) gas units. /// /// The number should be rounded down when converting from internal to external gas units. diff --git a/aptos-move/aptos-gas-profiling/src/profiler.rs b/aptos-move/aptos-gas-profiling/src/profiler.rs index 46d7f03810b0eb..63b1a8bc70c466 100644 --- a/aptos-move/aptos-gas-profiling/src/profiler.rs +++ b/aptos-move/aptos-gas-profiling/src/profiler.rs @@ -7,9 +7,7 @@ use crate::log::{ }; use aptos_gas_algebra::{Fee, FeePerGasUnit, InternalGas, NumArgs, NumBytes}; use aptos_gas_meter::AptosGasMeter; -use aptos_types::{ - contract_event::ContractEvent, state_store::state_key::StateKey, write_set::WriteOpSize, -}; +use aptos_types::{state_store::state_key::StateKey, write_set::WriteOpSize}; use aptos_vm_types::change_set::VMChangeSet; use move_binary_format::{ errors::{Location, PartialVMResult, VMResult}, @@ -479,18 +477,6 @@ where delegate! { fn algebra(&self) -> &Self::Algebra; - - fn storage_fee_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - fn storage_fee_refund_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - fn storage_fee_for_state_bytes(&self, key: &StateKey, op: &WriteOpSize) -> Fee; - - fn storage_fee_per_event(&self, event: &ContractEvent) -> Fee; - - fn storage_discount_for_events(&self, total_cost: Fee) -> Fee; - - fn storage_fee_for_transaction_storage(&self, txn_size: NumBytes) -> Fee; } delegate_mut! { @@ -534,14 +520,17 @@ where return Ok(0.into()); } + let pricing = self.disk_space_pricing(); + let params = &self.vm_gas_params().txn; + // Writes let mut write_fee = Fee::new(0); let mut write_set_storage = vec![]; let mut total_refund = Fee::new(0); for (key, op_size, op_creation_metadata) in change_set.write_set_iter_mut() { - let slot_fee = self.storage_fee_for_state_slot(&op_size); - let slot_refund = self.storage_fee_refund_for_state_slot(&op_size); - let bytes_fee = self.storage_fee_for_state_bytes(key, &op_size); + let slot_fee = pricing.storage_fee_for_state_slot(params, &op_size); + let slot_refund = pricing.storage_fee_refund_for_state_slot(params, &op_size); + let bytes_fee = pricing.storage_fee_for_state_bytes(params, key, &op_size); Self::maybe_record_storage_deposit(op_creation_metadata, slot_fee); total_refund += slot_refund; @@ -561,20 +550,20 @@ where let mut event_fee = Fee::new(0); let mut event_fees = vec![]; for (event, _) in change_set.events().iter() { - let fee = self.storage_fee_per_event(event); + let fee = pricing.storage_fee_per_event(params, event); event_fees.push(EventStorage { ty: event.type_tag().clone(), cost: fee, }); event_fee += fee; } - let event_discount = self.storage_discount_for_events(event_fee); + let event_discount = pricing.storage_discount_for_events(params, event_fee); let event_fee_with_discount = event_fee .checked_sub(event_discount) .expect("discount should always be less than or equal to total amount"); // Txn - let txn_fee = self.storage_fee_for_transaction_storage(txn_size); + let txn_fee = pricing.storage_fee_for_transaction_storage(params, txn_size); self.storage_fees = Some(StorageFees { total: write_fee + event_fee + txn_fee, diff --git a/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs b/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs index d835322f2af015..f2be95b29699b3 100644 --- a/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs +++ b/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs @@ -9,9 +9,6 @@ use aptos_gas_algebra::{ AbstractValueSize, Fee, FeePerByte, FeePerGasUnit, FeePerSlot, Gas, GasExpression, GasScalingFactor, GasUnit, NumSlots, }; -use aptos_types::{ - contract_event::ContractEvent, state_store::state_key::StateKey, write_set::WriteOpSize, -}; use move_core_types::gas_algebra::{ InternalGas, InternalGasPerArg, InternalGasPerByte, InternalGasUnit, NumBytes, ToUnitWithParams, }; @@ -194,51 +191,6 @@ impl TransactionGasParameters { } } - pub fn storage_fee_for_slot(&self, op_size: &WriteOpSize) -> Fee { - use WriteOpSize::*; - - match op_size { - Creation { .. } => self.storage_fee_per_state_slot_create * NumSlots::new(1), - Modification { .. } | Deletion | DeletionWithDeposit { .. } => 0.into(), - } - } - - pub fn storage_fee_refund_for_slot(&self, op_size: &WriteOpSize) -> Fee { - op_size.deletion_deposit().map_or(0.into(), Fee::new) - } - - /// Maybe value size is None for deletion Ops. - pub fn storage_fee_for_bytes(&self, key: &StateKey, op_size: &WriteOpSize) -> Fee { - if let Some(value_size) = op_size.write_len() { - let size = NumBytes::new(key.size() as u64) + NumBytes::new(value_size); - if let Some(excess) = size.checked_sub(self.free_write_bytes_quota) { - return excess * self.storage_fee_per_excess_state_byte; - } - } - - 0.into() - } - - /// New formula to charge storage fee for an event, measured in APT. - pub fn storage_fee_per_event(&self, event: &ContractEvent) -> Fee { - NumBytes::new(event.size() as u64) * self.storage_fee_per_event_byte - } - - pub fn storage_discount_for_events(&self, total_cost: Fee) -> Fee { - std::cmp::min( - total_cost, - self.free_event_bytes_quota * self.storage_fee_per_event_byte, - ) - } - - /// New formula to charge storage fee for transaction, measured in APT. - pub fn storage_fee_for_transaction_storage(&self, txn_size: NumBytes) -> Fee { - txn_size - .checked_sub(self.large_transaction_cutoff) - .unwrap_or(NumBytes::zero()) - * self.storage_fee_per_transaction_byte - } - /// Calculate the intrinsic gas for the transaction based upon its size in bytes. pub fn calculate_intrinsic_gas( &self, diff --git a/aptos-move/aptos-memory-usage-tracker/src/lib.rs b/aptos-move/aptos-memory-usage-tracker/src/lib.rs index e8ab4f6f44c153..3a4ac1895532cc 100644 --- a/aptos-move/aptos-memory-usage-tracker/src/lib.rs +++ b/aptos-move/aptos-memory-usage-tracker/src/lib.rs @@ -4,8 +4,7 @@ use aptos_gas_algebra::{AbstractValueSize, Fee, FeePerGasUnit, InternalGas, NumArgs, NumBytes}; use aptos_gas_meter::AptosGasMeter; use aptos_types::{ - account_config::CORE_CODE_ADDRESS, contract_event::ContractEvent, - state_store::state_key::StateKey, write_set::WriteOpSize, + account_config::CORE_CODE_ADDRESS, state_store::state_key::StateKey, write_set::WriteOpSize, }; use move_binary_format::{ errors::{PartialVMError, PartialVMResult, VMResult}, @@ -462,18 +461,6 @@ where delegate! { fn algebra(&self) -> &Self::Algebra; - - fn storage_fee_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - fn storage_fee_refund_for_state_slot(&self, op: &WriteOpSize) -> Fee; - - fn storage_fee_for_state_bytes(&self, key: &StateKey, op: &WriteOpSize) -> Fee; - - fn storage_fee_per_event(&self, event: &ContractEvent) -> Fee; - - fn storage_discount_for_events(&self, total_cost: Fee) -> Fee; - - fn storage_fee_for_transaction_storage(&self, txn_size: NumBytes) -> Fee; } delegate_mut! { diff --git a/aptos-move/aptos-vm-types/src/storage/mod.rs b/aptos-move/aptos-vm-types/src/storage/mod.rs index 12a8dfb5a8f2fa..d23297d1dc9251 100644 --- a/aptos-move/aptos-vm-types/src/storage/mod.rs +++ b/aptos-move/aptos-vm-types/src/storage/mod.rs @@ -4,18 +4,21 @@ use crate::storage::{ change_set_configs::ChangeSetConfigs, io_pricing::{IoPricing, IoPricingV3}, + space_pricing::{create_disk_space_pricing, DiskSpacePricing}, }; use aptos_gas_schedule::{AptosGasParameters, LATEST_GAS_FEATURE_VERSION}; use aptos_types::on_chain_config::ConfigStorage; use move_core_types::gas_algebra::NumBytes; -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; pub mod change_set_configs; pub mod io_pricing; +pub mod space_pricing; #[derive(Clone, Debug)] pub struct StorageGasParameters { pub io_pricing: IoPricing, + pub space_pricing: Arc, pub change_set_configs: ChangeSetConfigs, } @@ -26,10 +29,12 @@ impl StorageGasParameters { config_storage: &impl ConfigStorage, ) -> Self { let io_pricing = IoPricing::new(feature_version, gas_params, config_storage); + let space_pricing = create_disk_space_pricing(feature_version); let change_set_configs = ChangeSetConfigs::new(feature_version, gas_params); Self { io_pricing, + space_pricing, change_set_configs, } } @@ -40,6 +45,7 @@ impl StorageGasParameters { feature_version: LATEST_GAS_FEATURE_VERSION, free_write_bytes_quota, }), + space_pricing: create_disk_space_pricing(LATEST_GAS_FEATURE_VERSION), change_set_configs: ChangeSetConfigs::unlimited_at_gas_feature_version( LATEST_GAS_FEATURE_VERSION, ), diff --git a/aptos-move/aptos-vm-types/src/storage/space_pricing.rs b/aptos-move/aptos-vm-types/src/storage/space_pricing.rs new file mode 100644 index 00000000000000..7bdd996b7e1ff3 --- /dev/null +++ b/aptos-move/aptos-vm-types/src/storage/space_pricing.rs @@ -0,0 +1,137 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_gas_algebra::{Fee, NumSlots}; +use aptos_gas_schedule::TransactionGasParameters; +use aptos_types::{ + contract_event::ContractEvent, state_store::state_key::StateKey, write_set::WriteOpSize, +}; +use move_core_types::gas_algebra::NumBytes; +use std::{fmt::Debug, sync::Arc}; + +pub trait DiskSpacePricing: Debug + Send + Sync + 'static { + /// Calculates the storage fee for a state slot allocation. + fn storage_fee_for_state_slot( + &self, + params: &TransactionGasParameters, + op_size: &WriteOpSize, + ) -> Fee; + + /// Calculates the storage fee refund for a state slot deallocation. + fn storage_fee_refund_for_state_slot( + &self, + params: &TransactionGasParameters, + op_size: &WriteOpSize, + ) -> Fee; + + /// Calculates the storage fee for state byte. + fn storage_fee_for_state_bytes( + &self, + params: &TransactionGasParameters, + key: &StateKey, + op_size: &WriteOpSize, + ) -> Fee; + + /// Calculates the storage fee for an event. + fn storage_fee_per_event( + &self, + params: &TransactionGasParameters, + event: &ContractEvent, + ) -> Fee; + + /// Calculates the discount applied to the event storage fees, based on a free quota. + fn storage_discount_for_events( + &self, + params: &TransactionGasParameters, + total_cost: Fee, + ) -> Fee; + + /// Calculates the storage fee for the transaction. + fn storage_fee_for_transaction_storage( + &self, + params: &TransactionGasParameters, + txn_size: NumBytes, + ) -> Fee; +} + +pub fn create_disk_space_pricing(gas_feature_version: u64) -> Arc { + let pricing = match gas_feature_version { + 0..=6 => unimplemented!( + "Storage Fee not supported for gas_feature_version {gas_feature_version}" + ), + 7.. => V1, + }; + Arc::new(pricing) +} + +#[derive(Clone, Debug)] +pub struct V1; + +impl DiskSpacePricing for V1 { + fn storage_fee_for_state_slot( + &self, + params: &TransactionGasParameters, + op_size: &WriteOpSize, + ) -> Fee { + use WriteOpSize::*; + + match op_size { + Creation { .. } => params.storage_fee_per_state_slot_create * NumSlots::new(1), + Modification { .. } | Deletion | DeletionWithDeposit { .. } => 0.into(), + } + } + + fn storage_fee_refund_for_state_slot( + &self, + _params: &TransactionGasParameters, + op_size: &WriteOpSize, + ) -> Fee { + op_size.deletion_deposit().map_or(0.into(), Fee::new) + } + + fn storage_fee_for_state_bytes( + &self, + params: &TransactionGasParameters, + key: &StateKey, + op_size: &WriteOpSize, + ) -> Fee { + if let Some(value_size) = op_size.write_len() { + let size = NumBytes::new(key.size() as u64) + NumBytes::new(value_size); + if let Some(excess) = size.checked_sub(params.free_write_bytes_quota) { + return excess * params.storage_fee_per_excess_state_byte; + } + } + + 0.into() + } + + fn storage_fee_per_event( + &self, + params: &TransactionGasParameters, + event: &ContractEvent, + ) -> Fee { + NumBytes::new(event.size() as u64) * params.storage_fee_per_event_byte + } + + fn storage_discount_for_events( + &self, + params: &TransactionGasParameters, + total_cost: Fee, + ) -> Fee { + std::cmp::min( + total_cost, + params.free_event_bytes_quota * params.storage_fee_per_event_byte, + ) + } + + fn storage_fee_for_transaction_storage( + &self, + params: &TransactionGasParameters, + txn_size: NumBytes, + ) -> Fee { + txn_size + .checked_sub(params.large_transaction_cutoff) + .unwrap_or(NumBytes::zero()) + * params.storage_fee_per_transaction_byte + } +}