Skip to content

Commit

Permalink
Cherry-pick: limits & gas for dependencies (#12166)
Browse files Browse the repository at this point in the history
* [gas] add gas charges for dependencies

* [gas] optimize dependency gas by avoid allocating new ModuleId

* [gas] fix dependency gas for module publishing + minor gas profiler improvements

* [loader] Test gas charging logic for transitive deps

---------

Co-authored-by: Runtian Zhou <[email protected]>
  • Loading branch information
vgao1996 and runtian-zhou authored Mar 5, 2024
1 parent b7f95c3 commit 53c85b3
Show file tree
Hide file tree
Showing 45 changed files with 1,357 additions and 186 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion aptos-move/aptos-abstract-gas-usage/src/algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

use aptos_gas_algebra::{
DynamicExpression, Fee, FeePerGasUnit, GasExpression, InternalGas, InternalGasUnit, Octa,
DynamicExpression, Fee, FeePerGasUnit, GasExpression, InternalGas, InternalGasUnit, NumBytes,
Octa,
};
use aptos_gas_meter::GasAlgebra;
use aptos_gas_schedule::VMGasParameters;
Expand Down Expand Up @@ -80,6 +81,10 @@ impl<A: GasAlgebra> GasAlgebra for CalibrationAlgebra<A> {
.charge_storage_fee(abstract_amount, gas_unit_price)
}

fn count_dependency(&mut self, size: NumBytes) -> PartialVMResult<()> {
self.base.count_dependency(size)
}

fn execution_gas_used(&self) -> InternalGas {
self.base.execution_gas_used()
}
Expand Down
5 changes: 5 additions & 0 deletions aptos-move/aptos-gas-algebra/src/algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ pub type FeePerSlot = GasQuantity<UnitDiv<Octa, Slot>>;

pub type FeePerByte = GasQuantity<UnitDiv<Octa, Byte>>;

/// Unit of module
pub enum Module {}

pub type NumModules = GasQuantity<Module>;

/***************************************************************************************************
* Unit Conversion
*
Expand Down
22 changes: 21 additions & 1 deletion aptos-move/aptos-gas-meter/src/algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::traits::GasAlgebra;
use aptos_gas_algebra::{Fee, FeePerGasUnit, Gas, GasExpression, Octa};
use aptos_gas_algebra::{Fee, FeePerGasUnit, Gas, GasExpression, NumBytes, NumModules, Octa};
use aptos_gas_schedule::VMGasParameters;
use aptos_logger::error;
use aptos_vm_types::storage::{
Expand Down Expand Up @@ -32,6 +32,9 @@ pub struct StandardGasAlgebra {
storage_fee_in_internal_units: InternalGas,
// The storage fee consumed by the storage operations.
storage_fee_used: Fee,

num_dependencies: NumModules,
total_dependency_size: NumBytes,
}

impl StandardGasAlgebra {
Expand All @@ -53,6 +56,8 @@ impl StandardGasAlgebra {
io_gas_used: 0.into(),
storage_fee_in_internal_units: 0.into(),
storage_fee_used: 0.into(),
num_dependencies: 0.into(),
total_dependency_size: 0.into(),
}
}
}
Expand Down Expand Up @@ -234,6 +239,21 @@ impl GasAlgebra for StandardGasAlgebra {
Ok(())
}

fn count_dependency(&mut self, size: NumBytes) -> PartialVMResult<()> {
if self.feature_version >= 15 {
self.num_dependencies += 1.into();
self.total_dependency_size += size;

if self.num_dependencies > self.vm_gas_params.txn.max_num_dependencies {
return Err(PartialVMError::new(StatusCode::DEPENDENCY_LIMIT_REACHED));
}
if self.total_dependency_size > self.vm_gas_params.txn.max_total_dependency_size {
return Err(PartialVMError::new(StatusCode::DEPENDENCY_LIMIT_REACHED));
}
}
Ok(())
}

fn execution_gas_used(&self) -> InternalGas {
self.execution_gas_used
}
Expand Down
23 changes: 23 additions & 0 deletions aptos-move/aptos-gas-meter/src/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use move_binary_format::{
use move_core_types::{
account_address::AccountAddress,
gas_algebra::{InternalGas, NumArgs, NumBytes},
identifier::IdentStr,
language_storage::ModuleId,
vm_status::StatusCode,
};
Expand Down Expand Up @@ -469,6 +470,28 @@ where

self.algebra.charge_execution(cost)
}

#[inline]
fn charge_dependency(
&mut self,
_is_new: bool,
addr: &AccountAddress,
_name: &IdentStr,
size: NumBytes,
) -> PartialVMResult<()> {
// Modules under special addresses are considered system modules that should always
// be loaded, and are therefore excluded from gas charging.
//
// TODO: 0xA550C18 is a legacy system address we used, but it is currently not covered by
// `.is_special()`. We should double check if this address still needs special
// treatment.
if self.feature_version() >= 15 && !addr.is_special() {
self.algebra
.charge_execution(DEPENDENCY_PER_MODULE + DEPENDENCY_PER_BYTE * size)?;
self.algebra.count_dependency(size)?;
}
Ok(())
}
}

impl<A> AptosGasMeter for StandardGasMeter<A>
Expand Down
3 changes: 3 additions & 0 deletions aptos-move/aptos-gas-meter/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ pub trait GasAlgebra {
gas_unit_price: FeePerGasUnit,
) -> PartialVMResult<()>;

/// Counts a dependency against the limits.
fn count_dependency(&mut self, size: NumBytes) -> PartialVMResult<()>;

/// Returns the amount of gas used under the execution category.
fn execution_gas_used(&self) -> InternalGas;

Expand Down
27 changes: 25 additions & 2 deletions aptos-move/aptos-gas-profiling/src/erased.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::{
log::{
CallFrame, EventStorage, ExecutionAndIOCosts, ExecutionGasEvent, StorageFees, WriteStorage,
WriteTransient,
CallFrame, Dependency, EventStorage, ExecutionAndIOCosts, ExecutionGasEvent, StorageFees,
WriteStorage, WriteTransient,
},
render::Render,
FrameName, TransactionGasLog,
Expand Down Expand Up @@ -191,12 +191,35 @@ impl WriteTransient {
}
}

impl Dependency {
fn to_erased(&self) -> Node<InternalGas> {
Node::new(
format!(
"{}{}",
Render(&self.id),
if self.is_new { " (new)" } else { "" }
),
self.cost,
)
}
}

impl ExecutionAndIOCosts {
/// Convert the gas log into a type-erased representation.
pub fn to_erased(&self) -> TypeErasedExecutionAndIoCosts {
let mut nodes = vec![];

nodes.push(Node::new("intrinsic", self.intrinsic_cost));

if !self.dependencies.is_empty() {
let deps = Node::new_with_children(
"dependencies",
0,
self.dependencies.iter().map(|dep| dep.to_erased()),
);
nodes.push(deps);
}

nodes.push(self.call_graph.to_erased());

let writes = Node::new_with_children(
Expand Down
11 changes: 11 additions & 0 deletions aptos-move/aptos-gas-profiling/src/flamegraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ impl ExecutionAndIOCosts {
path: &'a mut Vec<String>,
}

for dep in &self.dependencies {
lines.push(
format!(
"dependencies;{}{}",
Render(&dep.id),
if dep.is_new { "(new)" } else { "" }
),
dep.cost,
)
}

impl<'a> Rec<'a> {
fn visit(&mut self, frame: &CallFrame) {
self.path.push(format!("{}", frame.name));
Expand Down
39 changes: 27 additions & 12 deletions aptos-move/aptos-gas-profiling/src/log.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use aptos_gas_algebra::{Fee, GasScalingFactor, InternalGas};
use aptos_gas_algebra::{Fee, GasScalingFactor, InternalGas, NumBytes};
use aptos_types::state_store::state_key::StateKey;
use move_binary_format::{file_format::CodeOffset, file_format_common::Opcodes};
use move_core_types::{
Expand All @@ -13,7 +13,7 @@ use smallvec::{smallvec, SmallVec};

/// An event occurred during the execution of a function, along with the
/// gas cost associated with it, if any.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ExecutionGasEvent {
/// A special event indicating that the program counter has moved to
/// a specific offset. This is emitted by the branch instructions
Expand Down Expand Up @@ -42,7 +42,7 @@ pub enum ExecutionGasEvent {

/// An enum representing the name of a call frame.
/// Could be either a script or a function.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum FrameName {
Script,
Function {
Expand All @@ -54,7 +54,7 @@ pub enum FrameName {

/// A struct containing information about a function call, including the name of the
/// function and all gas events that happened during the call.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CallFrame {
pub name: FrameName,
pub events: Vec<ExecutionGasEvent>,
Expand All @@ -63,49 +63,60 @@ pub struct CallFrame {
/// The type of an operation performed on a storage item.
///
/// Possible values: Creation, Modification & Deletion.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum WriteOpType {
Creation,
Modification,
Deletion,
}

/// Struct representing the transient (IO) cost of a write operation.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct WriteTransient {
pub key: StateKey,
pub op_type: WriteOpType,
pub cost: InternalGas,
}

/// Struct representing the storage cost of a write operation.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct WriteStorage {
pub key: StateKey,
pub op_type: WriteOpType,
pub cost: Fee,
pub refund: Fee,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
/// Struct representing the storage cost of an event.
pub struct EventStorage {
pub ty: TypeTag,
pub cost: Fee,
}

#[derive(Debug)]
#[derive(Debug, Clone)]
/// Struct representing the cost of a dependency.
pub struct Dependency {
pub is_new: bool,
pub id: ModuleId,
pub size: NumBytes,
pub cost: InternalGas,
}

/// Struct containing all execution and io costs.
#[derive(Debug, Clone)]
pub struct ExecutionAndIOCosts {
pub gas_scaling_factor: GasScalingFactor,
pub total: InternalGas,

pub intrinsic_cost: InternalGas,
pub dependencies: Vec<Dependency>,
pub call_graph: CallFrame,
pub write_set_transient: Vec<WriteTransient>,
}

#[derive(Debug)]
// Struct containing all types of storage fees.
#[derive(Debug, Clone)]
/// Struct containing all types of storage fees.
pub struct StorageFees {
pub total: Fee,
pub total_refund: Fee,
Expand All @@ -118,7 +129,7 @@ pub struct StorageFees {

/// A complete log that contains all gas-related information about a transaction, including
/// the intrinsic cost, a detailed execution log and the write set costs.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TransactionGasLog {
pub exec_io: ExecutionAndIOCosts,
pub storage: StorageFees,
Expand Down Expand Up @@ -219,6 +230,10 @@ impl ExecutionAndIOCosts {

total += self.intrinsic_cost;

for dep in &self.dependencies {
total += dep.cost;
}

for op in self.gas_events() {
match op {
Loc(..) | Call(..) => (),
Expand Down
Loading

0 comments on commit 53c85b3

Please sign in to comment.