Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement fee-related changes of CAP-66. #1522

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ wasmparser = "=0.116.1"
[workspace.dependencies.stellar-xdr]
version = "=22.1.0"
git = "https://github.com/stellar/rs-stellar-xdr"
rev = "28391e81229ad20a3d9ec56e1e1f382205335085"
rev = "c356a8cdcf59e91bd2224edda45eff337335ccfb"
default-features = false

[workspace.dependencies.wasmi]
Expand Down
6 changes: 5 additions & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ ignore = [
# This ignores an error due to the now unmaintained derivative crate. This
# crate is a dependency for some of the ark crates, so we need to ignore the
# advisory.
"RUSTSEC-2024-0388"
"RUSTSEC-2024-0388",

# This ignores the advisory re unmaintained `paste` crate that is a
# transitive dependency of several crates we depend on.
"RUSTSEC-2024-0436"
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-git-revision = "0.0.6"

[dependencies]
soroban-env-macros = { workspace = true }
stellar-xdr = { workspace = true, default-features = false, features = [ "curr" ] }
stellar-xdr = { workspace = true, default-features = false, features = [ "next" ] }
wasmi = { workspace = true, optional = true }
wasmparser = { workspace = true, optional = true}
serde = { version = "1.0.192", features = ["derive"], optional = true }
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub use storage_type::StorageType;

// Re-export the XDR definitions of a specific version -- curr or next -- of the xdr crate.
#[cfg(not(feature = "next"))]
pub use stellar_xdr::curr as xdr;
pub use stellar_xdr::next as xdr;
#[cfg(feature = "next")]
pub use stellar_xdr::next as xdr;

Expand Down
4 changes: 2 additions & 2 deletions soroban-env-common/src/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,8 @@ pub fn is_small_i256_parts(i: &Int256Parts) -> bool {
}

pub fn u256_from_pieces(hi_hi: u64, hi_lo: u64, lo_hi: u64, lo_lo: u64) -> U256 {
let high = (u128::from(hi_hi)) << 64 | u128::from(hi_lo);
let low = (u128::from(lo_hi)) << 64 | u128::from(lo_lo);
let high = (u128::from(hi_hi) << 64) | u128::from(hi_lo);
let low = (u128::from(lo_hi) << 64) | u128::from(lo_lo);
U256::from_words(high, low)
}

Expand Down
2 changes: 1 addition & 1 deletion soroban-env-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ p256 = {version = "0.13.2", default-features = false, features = ["alloc"]}
[dev-dependencies.stellar-xdr]
version = "=22.1.0"
git = "https://github.com/stellar/rs-stellar-xdr"
rev = "28391e81229ad20a3d9ec56e1e1f382205335085"
rev = "c356a8cdcf59e91bd2224edda45eff337335ccfb"
default-features = false
features = ["arbitrary"]

Expand Down
19 changes: 19 additions & 0 deletions soroban-env-host/src/budget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ impl BudgetImpl {
})
}

pub(crate) fn get_memory_cost(
&self,
ty: ContractCostType,
iterations: u64,
input: Option<u64>,
) -> Result<u64, HostError> {
self.mem_bytes.get_cost(ty, iterations, input)
}

pub fn charge(
&mut self,
ty: ContractCostType,
Expand Down Expand Up @@ -1155,6 +1164,16 @@ impl Budget {
self.0.try_borrow_mut_or_err()?.charge(ty, 1, input)
}

pub(crate) fn get_memory_cost(
&self,
ty: ContractCostType,
input: Option<u64>,
) -> Result<u64, HostError> {
self.0
.try_borrow_mut_or_err()?
.get_memory_cost(ty, 1, input)
}

/// Runs a user provided closure in shadow mode -- all metering is done
/// through the shadow budget.
///
Expand Down
9 changes: 9 additions & 0 deletions soroban-env-host/src/budget/dimension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ impl BudgetDimension {
Ok(amount)
}

pub(crate) fn get_cost(
&self,
ty: ContractCostType,
iterations: u64,
input: Option<u64>,
) -> Result<u64, HostError> {
self.get_cost_model(ty)?.evaluate(iterations, input)
}

// Resets all model parameters to zero (so that we can override and test individual ones later).
#[cfg(any(test, feature = "testutils", feature = "bench"))]
pub(crate) fn reset_models(&mut self) {
Expand Down
74 changes: 54 additions & 20 deletions soroban-env-host/src/e2e_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/// host functions.
use std::{cmp::max, rc::Rc};

use crate::storage::EntryWithLiveUntil;
#[cfg(any(test, feature = "recording_mode"))]
use crate::{
auth::RecordedAuthPayload,
Expand All @@ -31,6 +30,7 @@ use crate::{
DiagnosticLevel, Error, Host, HostError, LedgerInfo, MeteredOrdMap,
};
use crate::{ledger_info::get_key_durability, ModuleCache};
use crate::{storage::EntryWithLiveUntil, vm::wasm_module_memory_cost};
#[cfg(any(test, feature = "recording_mode"))]
use sha2::{Digest, Sha256};

Expand Down Expand Up @@ -89,20 +89,24 @@ pub struct InvokeHostFunctionRecordingModeResult {
pub struct LedgerEntryChange {
/// Whether the ledger entry is read-only, as defined by the footprint.
pub read_only: bool,

/// Entry key encoded as `LedgerKey` XDR.
pub encoded_key: Vec<u8>,
/// Size of the old entry in bytes. This is size of `LedgerEntry` encoded
/// XDR.
pub old_entry_size_bytes: u32,
/// Size of the 'old' entry to use in the rent computations.
/// This is the size of the encoded entry XDR for all of the entries besides
/// contract code, for which the module in-memory size is used instead.
pub old_entry_size_bytes_for_rent: u32,
/// New value of the ledger entry encoded as `LedgerEntry` XDR.
/// Only set for non-removed, non-readonly values, otherwise `None`.
pub encoded_new_value: Option<Vec<u8>>,
/// Size of the 'new' entry to use in the rent computations.
/// This is the size of the encoded entry XDR (i.e. length of `encoded_new_value`)
/// for all of the entries besides contract code, for which the module
/// in-memory size is used instead.
pub new_entry_size_bytes_for_rent: u32,
/// Change of the live until state of the entry.
/// Only set for entries that have a TTL, otherwise `None`.
pub ttl_change: Option<LedgerEntryLiveUntilChange>,
}

/// Represents the live until-related state of the entry.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct LedgerEntryLiveUntilChange {
Expand Down Expand Up @@ -162,7 +166,9 @@ pub fn get_ledger_changes(
if let Some((old_entry, old_live_until_ledger)) = entry_with_live_until {
let mut buf = vec![];
metered_write_xdr(budget, old_entry.as_ref(), &mut buf)?;
entry_change.old_entry_size_bytes = buf.len() as u32;

entry_change.old_entry_size_bytes_for_rent =
entry_size_for_rent(budget, &old_entry, buf.len() as u32)?;

if let Some(ref mut ttl_change) = &mut entry_change.ttl_change {
ttl_change.old_live_until_ledger =
Expand All @@ -188,6 +194,8 @@ pub fn get_ledger_changes(
if let Some((entry, _)) = entry_with_live_until_ledger {
let mut entry_buf = vec![];
metered_write_xdr(budget, entry.as_ref(), &mut entry_buf)?;
entry_change.new_entry_size_bytes_for_rent =
entry_size_for_rent(budget, &entry, entry_buf.len() as u32)?;
entry_change.encoded_new_value = Some(entry_buf);
}
}
Expand Down Expand Up @@ -243,15 +251,15 @@ pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec<LedgerE
if let (Some(ttl_change), optional_encoded_new_value) =
(&entry_change.ttl_change, &entry_change.encoded_new_value)
{
let new_size_bytes = if let Some(encoded_new_value) = optional_encoded_new_value {
encoded_new_value.len() as u32
let new_size_bytes_for_rent = if optional_encoded_new_value.is_some() {
entry_change.new_entry_size_bytes_for_rent
} else {
entry_change.old_entry_size_bytes
entry_change.old_entry_size_bytes_for_rent
};

// Skip the entry if 1. it is not extended and 2. the entry size has not increased
if ttl_change.old_live_until_ledger >= ttl_change.new_live_until_ledger
&& entry_change.old_entry_size_bytes >= new_size_bytes
&& entry_change.old_entry_size_bytes_for_rent >= new_size_bytes_for_rent
{
return None;
}
Expand All @@ -260,8 +268,8 @@ pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec<LedgerE
ttl_change.durability,
ContractDataDurability::Persistent
),
old_size_bytes: entry_change.old_entry_size_bytes,
new_size_bytes,
old_size_bytes: entry_change.old_entry_size_bytes_for_rent,
new_size_bytes: new_size_bytes_for_rent,
old_live_until_ledger: ttl_change.old_live_until_ledger,
new_live_until_ledger: ttl_change.new_live_until_ledger,
})
Expand All @@ -272,6 +280,27 @@ pub fn extract_rent_changes(ledger_changes: &[LedgerEntryChange]) -> Vec<LedgerE
.collect()
}

/// Helper for computing the size of the ledger entry to be used in rent
/// computations.
///
/// This returns the size of the Wasm module in memory for the contract code
/// entries and the provided XDR size of the entry otherwise.
///
/// Note, that this doesn't compute the XDR size because the recomputation
/// might be costly.
pub fn entry_size_for_rent(
budget: &Budget,
entry: &LedgerEntry,
entry_xdr_size: u32,
) -> Result<u32, HostError> {
Ok(match &entry.data {
LedgerEntryData::ContractCode(contract_code_entry) => {
wasm_module_memory_cost(budget, contract_code_entry)?.min(u32::MAX as u64) as u32
}
_ => entry_xdr_size,
})
}

/// Invokes a host function within a fresh host instance.
///
/// This collects the necessary inputs as encoded XDR and returns the outputs
Expand Down Expand Up @@ -644,18 +673,25 @@ pub fn invoke_host_function_in_recording_mode(
}
}

let (footprint, read_bytes, init_ttl_map) = host.with_mut_storage(|storage| {
let (footprint, disk_read_bytes, init_ttl_map) = host.with_mut_storage(|storage| {
let footprint = storage_footprint_to_ledger_footprint(&storage.footprint)?;
let _footprint_from_xdr = build_storage_footprint_from_xdr(&budget, footprint.clone())?;

let mut encoded_ledger_entries = Vec::with_capacity(storage.footprint.0.len());
let mut encoded_ttl_entries = Vec::with_capacity(storage.footprint.0.len());
let mut read_bytes = 0_u32;
let mut disk_read_bytes = 0_u32;
for (lk, _) in &storage.footprint.0 {
let entry_with_live_until = ledger_snapshot.get(lk)?;
if let Some((le, live_until)) = entry_with_live_until {
let encoded_le = host.to_xdr_non_metered(&*le)?;
read_bytes = read_bytes.saturating_add(encoded_le.len() as u32);
match &le.data {
LedgerEntryData::ContractData(_) | LedgerEntryData::ContractCode(_) => (),
_ => {
// Non-Soroban entries are counted towards disk read bytes.
disk_read_bytes = disk_read_bytes.saturating_add(encoded_le.len() as u32);
}
}

encoded_ledger_entries.push(encoded_le);
if let Some(live_until_ledger) = live_until {
let key_xdr = host.to_xdr_non_metered(lk.as_ref())?;
Expand All @@ -680,12 +716,12 @@ pub fn invoke_host_function_in_recording_mode(
ledger_seq,
)?;
let _init_storage_clone = init_storage.metered_clone(budget)?;
Ok((footprint, read_bytes, init_ttl_map))
Ok((footprint, disk_read_bytes, init_ttl_map))
})?;
let mut resources = SorobanResources {
footprint,
instructions: 0,
read_bytes,
read_bytes: disk_read_bytes,
write_bytes: 0,
};
let _resources_roundtrip: SorobanResources =
Expand Down Expand Up @@ -850,13 +886,11 @@ fn build_storage_map_from_xdr_ledger_entries<T: AsRef<[u8]>, I: ExactSizeIterato
budget,
)?;
let key = Rc::metered_new(ledger_entry_to_ledger_key(&le, budget)?, budget)?;

if !ttl_buf.as_ref().is_empty() {
let ttl_entry = Rc::metered_new(
metered_from_xdr_with_budget::<TtlEntry>(ttl_buf.as_ref(), budget)?,
budget,
)?;

if ttl_entry.live_until_ledger_seq < ledger_num {
return Err(Error::from_type_and_code(
ScErrorType::Storage,
Expand Down
17 changes: 13 additions & 4 deletions soroban-env-host/src/e2e_testutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ pub fn wasm_entry(wasm: &[u8]) -> LedgerEntry {
wasm_entry_with_refined_contract_cost_inputs(wasm, true)
}

pub(crate) fn wasm_entry_with_refined_contract_cost_inputs(
pub(crate) fn contract_code_entry_with_refined_contract_cost_inputs(
wasm: &[u8],
add_refined_cost_inputs: bool,
) -> LedgerEntry {
) -> ContractCodeEntry {
let ext = if !add_refined_cost_inputs {
ContractCodeEntryExt::V0
} else {
Expand All @@ -82,11 +82,20 @@ pub(crate) fn wasm_entry_with_refined_contract_cost_inputs(
ext: ExtensionPoint::V0,
})
};
ledger_entry(LedgerEntryData::ContractCode(ContractCodeEntry {
ContractCodeEntry {
ext,
hash: get_wasm_hash(wasm).try_into().unwrap(),
code: wasm.try_into().unwrap(),
}))
}
}

pub(crate) fn wasm_entry_with_refined_contract_cost_inputs(
wasm: &[u8],
add_refined_cost_inputs: bool,
) -> LedgerEntry {
ledger_entry(LedgerEntryData::ContractCode(
contract_code_entry_with_refined_contract_cost_inputs(wasm, add_refined_cost_inputs),
))
}

pub fn e2e_test_protocol_version() -> u32 {
Expand Down
Loading