Skip to content

Commit

Permalink
added formulas and moves constants (MystenLabs#4418)
Browse files Browse the repository at this point in the history
* added formulas and moves constants
  • Loading branch information
oxade authored Sep 1, 2022
1 parent 31e9436 commit b81509f
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 166 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::ops::{Add, Mul};
use std::ops::Mul;

use move_binary_format::errors::{PartialVMError, PartialVMResult};
use move_binary_format::file_format_common::Opcodes::{self};
use move_core_types::gas_algebra::{
AbstractMemorySize, GasQuantity, InternalGas, InternalGasPerAbstractMemoryUnit,
InternalGasUnit, NumArgs, NumBytes, ToUnit, ToUnitFractional,
AbstractMemorySize, InternalGas, InternalGasPerAbstractMemoryUnit, NumArgs, NumBytes,
};
use move_core_types::language_storage::ModuleId;
use move_core_types::vm_status::StatusCode;
use move_vm_types::gas::{GasMeter, SimpleInstruction};
use move_vm_types::views::{TypeView, ValueView};
use once_cell::sync::Lazy;

use crate::units_types::{CostTable, Gas, GasCost};
use move_binary_format::{
file_format::{
Bytecode, ConstantPoolIndex, FieldHandleIndex, FieldInstantiationIndex,
Expand All @@ -23,120 +23,6 @@ use move_binary_format::{
},
file_format_common::instruction_key,
};
use serde::{Deserialize, Serialize};

// NOTE: all values in this file are subject to change

// Maximum number of events a call can emit
pub const MAX_NUM_EVENT_EMIT: u64 = 256;

// Maximum gas a TX can use
pub const MAX_TX_GAS: u64 = 1_000_000_000;

//
// Fixed costs: these are charged regardless of execution
//
// This is a flat fee
pub const BASE_TX_COST_FIXED: u64 = 1_000;
// This is charged per byte of the TX
pub const BASE_TX_COST_PER_BYTE: u64 = 10;

//
// Object access costs: These are for reading, writing, and verifying objects
//
// Cost to read an object per byte
pub const OBJ_ACCESS_COST_READ: u64 = 100;
// Cost to mutate an object per byte
pub const OBJ_ACCESS_COST_MUTATE: u64 = 100;
// Cost to delete an object per byte
pub const OBJ_ACCESS_COST_DELETE: u64 = 20;
// For checking locks. Charged per object
pub const OBJ_ACCESS_COST_VERIFY: u64 = 200;

//
// Object storage costs: These are for storing objects
//
// Cost to store an object per byte. This is refundable
pub const OBJ_DATA_COST_REFUNDABLE: u64 = 100;
// Cost to store metadata of objects per byte.
// This depends on the size of various fields including the effects
pub const OBJ_METADATA_COST_REFUNDABLE: u64 = 100;

//
// Consensus costs: costs for TXes that use shared object
//
// Flat cost for consensus transactions
pub const CONSENSUS_COST: u64 = 1_000;

//
// Package verification & publish cost: when publishing a package
//
// Flat cost
pub const PACKAGE_PUBLISH_COST: u64 = 1_000;

//
// Native function costs
//
// TODO: need to refactor native gas calculation so it is extensible. Currently we
// have hardcoded here the stdlib natives.
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(u8)]
pub enum SuiNativeCostIndex {
EVENT_EMIT = 0,

OBJECT_BYTES_TO_ADDR = 1,
OBJECT_BORROW_UUID = 2,
OBJECT_DELETE_IMPL = 3,

TRANSFER_TRANSFER_INTERNAL = 4,
TRANSFER_FREEZE_OBJECT = 5,
TRANSFER_SHARE_OBJECT = 6,

TX_CONTEXT_DERIVE_ID = 7,
TX_CONTEXT_NEW_SIGNER_FROM_ADDR = 8,
}

// Native costs are currently flat
// TODO recalibrate wrt bytecode costs
pub fn _native_cost_schedule() -> Vec<GasCost> {
use SuiNativeCostIndex as N;

let mut native_table = vec![
// This is artificially chosen to limit too many event emits
// We will change this in https://github.com/MystenLabs/sui/issues/3341
(
N::EVENT_EMIT,
GasCost::new(MAX_TX_GAS / MAX_NUM_EVENT_EMIT, 1),
),
(N::OBJECT_BYTES_TO_ADDR, GasCost::new(30, 1)),
(N::OBJECT_BORROW_UUID, GasCost::new(150, 1)),
(N::OBJECT_DELETE_IMPL, GasCost::new(100, 1)),
(N::TRANSFER_TRANSFER_INTERNAL, GasCost::new(80, 1)),
(N::TRANSFER_FREEZE_OBJECT, GasCost::new(80, 1)),
(N::TRANSFER_SHARE_OBJECT, GasCost::new(80, 1)),
(N::TX_CONTEXT_DERIVE_ID, GasCost::new(110, 1)),
(N::TX_CONTEXT_NEW_SIGNER_FROM_ADDR, GasCost::new(200, 1)),
];
native_table.sort_by_key(|cost| cost.0 as u64);
native_table
.into_iter()
.map(|(_, cost)| cost)
.collect::<Vec<_>>()
}

pub enum GasUnit {}

pub type Gas = GasQuantity<GasUnit>;

impl ToUnit<InternalGasUnit> for GasUnit {
const MULTIPLIER: u64 = 1000;
}

impl ToUnitFractional<GasUnit> for InternalGasUnit {
const NOMINATOR: u64 = 1;
const DENOMINATOR: u64 = 1000;
}

/// The size in bytes for a non-string or address constant on the stack
pub const CONST_SIZE: AbstractMemorySize = AbstractMemorySize::new(16);
Expand All @@ -150,46 +36,6 @@ pub const STRUCT_SIZE: AbstractMemorySize = AbstractMemorySize::new(2);
/// For exists checks on data that doesn't exists this is the multiplier that is used.
pub const MIN_EXISTS_DATA_SIZE: AbstractMemorySize = AbstractMemorySize::new(100);

/// The cost tables, keyed by the serialized form of the bytecode instruction. We use the
/// serialized form as opposed to the instruction enum itself as the key since this will be the
/// on-chain representation of bytecode instructions in the future.
#[derive(Clone, Debug, Serialize, PartialEq, Eq, Deserialize)]
pub struct CostTable {
pub instruction_table: Vec<GasCost>,
}

impl CostTable {
#[inline]
pub fn instruction_cost(&self, instr_index: u8) -> &GasCost {
debug_assert!(instr_index > 0 && instr_index <= (self.instruction_table.len() as u8));
&self.instruction_table[(instr_index - 1) as usize]
}
}

/// The `GasCost` tracks:
/// - instruction cost: how much time/computational power is needed to perform the instruction
/// - memory cost: how much memory is required for the instruction, and storage overhead
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct GasCost {
pub instruction_gas: u64,
pub memory_gas: u64,
}

impl GasCost {
pub fn new(instruction_gas: u64, memory_gas: u64) -> Self {
Self {
instruction_gas,
memory_gas,
}
}

/// Convert a GasCost to a total gas charge in `InternalGas`.
#[inline]
pub fn total(&self) -> u64 {
self.instruction_gas.add(self.memory_gas)
}
}

static ZERO_COST_SCHEDULE: Lazy<CostTable> = Lazy::new(zero_cost_schedule);

/// The Move VM implementation of state for gas metering.
Expand Down
5 changes: 4 additions & 1 deletion crates/sui-cost-tables/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

pub mod tables;
pub mod bytecode_tables;
pub mod natives_tables;
pub mod non_execution_tables;
pub mod units_types;
58 changes: 58 additions & 0 deletions crates/sui-cost-tables/src/natives_tables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::{
non_execution_tables::{MAX_NUM_EVENT_EMIT, MAX_TX_GAS},
units_types::GasCost,
};

//
// Native function costs
//
// TODO: need to refactor native gas calculation so it is extensible. Currently we
// have hardcoded here the stdlib natives.
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(u8)]
pub enum SuiNativeCostIndex {
EVENT_EMIT = 0,

OBJECT_BYTES_TO_ADDR = 1,
OBJECT_BORROW_UUID = 2,
OBJECT_DELETE_IMPL = 3,

TRANSFER_TRANSFER_INTERNAL = 4,
TRANSFER_FREEZE_OBJECT = 5,
TRANSFER_SHARE_OBJECT = 6,

TX_CONTEXT_DERIVE_ID = 7,
TX_CONTEXT_NEW_SIGNER_FROM_ADDR = 8,
}

// Native costs are currently flat
// TODO recalibrate wrt bytecode costs
pub fn _native_cost_schedule() -> Vec<GasCost> {
use SuiNativeCostIndex as N;

let mut native_table = vec![
// This is artificially chosen to limit too many event emits
// We will change this in https://github.com/MystenLabs/sui/issues/3341
(
N::EVENT_EMIT,
GasCost::new(MAX_TX_GAS / MAX_NUM_EVENT_EMIT, 1),
),
(N::OBJECT_BYTES_TO_ADDR, GasCost::new(30, 1)),
(N::OBJECT_BORROW_UUID, GasCost::new(150, 1)),
(N::OBJECT_DELETE_IMPL, GasCost::new(100, 1)),
(N::TRANSFER_TRANSFER_INTERNAL, GasCost::new(80, 1)),
(N::TRANSFER_FREEZE_OBJECT, GasCost::new(80, 1)),
(N::TRANSFER_SHARE_OBJECT, GasCost::new(80, 1)),
(N::TX_CONTEXT_DERIVE_ID, GasCost::new(110, 1)),
(N::TX_CONTEXT_NEW_SIGNER_FROM_ADDR, GasCost::new(200, 1)),
];
native_table.sort_by_key(|cost| cost.0 as u64);
native_table
.into_iter()
.map(|(_, cost)| cost)
.collect::<Vec<_>>()
}
90 changes: 90 additions & 0 deletions crates/sui-cost-tables/src/non_execution_tables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// These tables cover parts of TX processing which do not involve the execution of a TX
// NOTE: all values in this file are subject to change

use move_core_types::gas_algebra::{Byte, InternalGas, InternalGasPerByte, InternalGasUnit};

use crate::units_types::LinearEquation;

// Maximum number of events a call can emit
pub const MAX_NUM_EVENT_EMIT: u64 = 256;

// Maximum gas a TX can use
pub const MAX_TX_GAS: u64 = 1_000_000_000;

//
// Fixed costs: these are charged regardless of execution
//
// This is a flat fee
pub const BASE_TX_COST_FIXED: u64 = 10_000;
// This is charged per byte of the TX
// 0 for now till we decide if we want size dependence
pub const BASE_TX_COST_PER_BYTE: u64 = 0;

//
// Object access costs: These are for reading, writing, and verifying objects
//
// Cost to read an object per byte
pub const OBJ_ACCESS_COST_READ_PER_BYTE: u64 = 15;
// Cost to mutate an object per byte
pub const OBJ_ACCESS_COST_MUTATE_PER_BYTE: u64 = 40;
// Cost to delete an object per byte
pub const OBJ_ACCESS_COST_DELETE_PER_BYTE: u64 = 40;
// For checking locks. Charged per object
pub const OBJ_ACCESS_COST_VERIFY_PER_BYTE: u64 = 200;

//
// Object storage costs: These are for storing objects
//
// Cost to store an object per byte. This is refundable
pub const OBJ_DATA_COST_REFUNDABLE: u64 = 100;
// Cost to store metadata of objects per byte.
// This depends on the size of various fields including the effects
pub const OBJ_METADATA_COST_NON_REFUNDABLE: u64 = 50;

//
// Consensus costs: costs for TXes that use shared object
//
// Flat cost for consensus transactions
pub const CONSENSUS_COST: u64 = 100_000;

//
// Package verification & publish cost: when publishing a package
//
// Flat cost
pub const PACKAGE_PUBLISH_COST_FIXED: u64 = 1_000;
// This is charged per byte of the package
pub const PACKAGE_PUBLISH_COST_PER_BYTE: u64 = 80;

pub const MAXIMUM_TX_GAS: InternalGas = InternalGas::new(MAX_TX_GAS);

pub const PUBLISH_COST_EQUATION: LinearEquation<InternalGasUnit, Byte> = publish_cost_equation();

pub const TRANSACTION_ACCEPTANCE_COST_EQUATION: LinearEquation<InternalGasUnit, Byte> =
transaction_acceptance_cost_equation();

const fn publish_cost_equation() -> LinearEquation<InternalGasUnit, Byte> {
let offset = InternalGas::new(PACKAGE_PUBLISH_COST_FIXED);
let slope = InternalGasPerByte::new(PACKAGE_PUBLISH_COST_PER_BYTE);

LinearEquation::new(
slope,
offset,
InternalGas::new(PACKAGE_PUBLISH_COST_FIXED),
InternalGas::new(MAX_TX_GAS),
)
}

const fn transaction_acceptance_cost_equation() -> LinearEquation<InternalGasUnit, Byte> {
let offset = InternalGas::new(BASE_TX_COST_FIXED);
let slope = InternalGasPerByte::new(BASE_TX_COST_PER_BYTE);

LinearEquation::new(
slope,
offset,
InternalGas::new(PACKAGE_PUBLISH_COST_FIXED),
InternalGas::new(MAX_TX_GAS),
)
}
Loading

0 comments on commit b81509f

Please sign in to comment.