Skip to content

Commit

Permalink
Move all the metered code to invoke host functions into a single host…
Browse files Browse the repository at this point in the history
…-side helper.

This helper can be invoked from any environment to get the metering data and results consistent with what Core will do.

Also provide utilities to build input data for computing the rent fees.
  • Loading branch information
dmkozh committed Jul 25, 2023
1 parent bf12f7b commit 4466087
Show file tree
Hide file tree
Showing 9 changed files with 543 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# target = "triple" # build for the target triple (ignored by `cargo install`)
# target-dir = "target" # path of where to place all generated artifacts
rustflags = [
"-Dwarnings",
# "-Dwarnings",
"-Aclippy::style",
# "-Dclippy::pedantic",
# "-Aclippy::module_name_repetitions",
Expand Down
7 changes: 5 additions & 2 deletions soroban-env-host/src/cost_runner/cost_types/val_ser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::hint::black_box;

use crate::{cost_runner::CostRunner, xdr::ContractCostType, xdr::ScVal};
use crate::{
cost_runner::CostRunner, host::metered_xdr::metered_write_xdr, xdr::ContractCostType,
xdr::ScVal,
};

pub struct ValSerRun;

Expand All @@ -18,7 +21,7 @@ impl CostRunner for ValSerRun {
) -> Self::RecycledType {
// Note the sample.1 is an empty vector, so metered_write_xdr includes allocation
// cost. This is how it's typically used so we are setting it up this way.
black_box(host.metered_write_xdr(&sample.0, &mut sample.1).unwrap());
black_box(metered_write_xdr(host.budget_ref(), &sample.0, &mut sample.1).unwrap());
sample
}

Expand Down
486 changes: 486 additions & 0 deletions soroban-env-host/src/e2e_invoke.rs

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion soroban-env-host/src/host/declared_size.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::rc::Rc;

use soroban_env_common::xdr::SorobanAuthorizationEntry;

use crate::{
events::{EventError, HostEvent, InternalContractEvent, InternalEvent},
host::Events,
Expand Down Expand Up @@ -147,7 +149,7 @@ impl_declared_size_type!(ContractEntryBodyType, 4);
impl_declared_size_type!(ExtensionPoint, 0);
impl_declared_size_type!(SorobanAuthorizedInvocation, 128);
impl_declared_size_type!(ScContractInstance, 64);

impl_declared_size_type!(SorobanAuthorizationEntry, 240);
// composite types

// Rc is an exception, nothing is being cloned. We approximate ref counter bump with the cost of
Expand Down Expand Up @@ -338,6 +340,11 @@ mod test {
expect!["8"].assert_eq(size_of::<Rc<ScVal>>().to_string().as_str());
expect!["64"].assert_eq(size_of::<Option<ScVal>>().to_string().as_str());
expect!["64"].assert_eq(size_of::<ScContractInstance>().to_string().as_str());
expect!["240"].assert_eq(
size_of::<SorobanAuthorizedInvocation>()
.to_string()
.as_str(),
);
}

// This is the actual test.
Expand Down Expand Up @@ -460,6 +467,7 @@ mod test {
assert_mem_size_le_declared_size!(ContractEntryBodyType);
assert_mem_size_le_declared_size!(ExtensionPoint);
assert_mem_size_le_declared_size!(SorobanAuthorizedInvocation);
assert_mem_size_le_declared_size!(SorobanAuthorizationEntry);
// composite types
assert_mem_size_le_declared_size!(&[ScVal]);
assert_mem_size_le_declared_size!((Val, ScVal));
Expand Down
3 changes: 2 additions & 1 deletion soroban-env-host/src/host/metered_clone.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{mem, rc::Rc};

use soroban_env_common::xdr::DepthLimiter;
use soroban_env_common::xdr::{DepthLimiter, SorobanAuthorizationEntry};

use crate::{
budget::Budget,
Expand Down Expand Up @@ -227,6 +227,7 @@ impl MeteredClone for EventError {}
impl MeteredClone for CreateContractArgs {}
impl MeteredClone for ContractIdPreimage {}
impl MeteredClone for SorobanAuthorizedInvocation {}
impl MeteredClone for SorobanAuthorizationEntry {}
// composite types
impl<T> MeteredClone for Rc<T> {}
impl<T> MeteredClone for &[T] {}
Expand Down
55 changes: 34 additions & 21 deletions soroban-env-host/src/host/metered_xdr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
budget::Budget,
xdr::ContractCostType,
xdr::{ReadXdr, ScBytes, WriteXdr},
BytesObject, Host, HostError,
Expand All @@ -11,7 +12,7 @@ use soroban_env_common::xdr::{
};

struct MeteredWrite<'a, W: Write> {
host: &'a Host,
budget: &'a Budget,
w: &'a mut W,
}

Expand All @@ -20,8 +21,8 @@ where
W: Write,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.host
.charge_budget(ContractCostType::ValSer, Some(buf.len() as u64))
self.budget
.charge(ContractCostType::ValSer, Some(buf.len() as u64))
.map_err(Into::<std::io::Error>::into)?;
self.w.write(buf)
}
Expand All @@ -32,30 +33,15 @@ where
}

impl Host {
pub(crate) fn metered_write_xdr(
&self,
obj: &impl WriteXdr,
w: &mut Vec<u8>,
) -> Result<(), HostError> {
let _span = tracy_span!("write xdr");
let mw = MeteredWrite { host: self, w };
let mut w = DepthLimitedWrite::new(mw, DEFAULT_XDR_RW_DEPTH_LIMIT);
// MeteredWrite above turned any budget failure into an IO error; we turn it
// back to a budget failure here, since there's really no "IO error" that can
// occur when writing to a Vec<u8>.
obj.write_xdr(&mut w)
.map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
}

pub(crate) fn metered_hash_xdr(&self, obj: &impl WriteXdr) -> Result<[u8; 32], HostError> {
pub fn metered_hash_xdr(&self, obj: &impl WriteXdr) -> Result<[u8; 32], HostError> {
let _span = tracy_span!("hash xdr");
let mut buf = vec![];
self.metered_write_xdr(obj, &mut buf)?;
metered_write_xdr(self.budget_ref(), obj, &mut buf)?;
self.charge_budget(ContractCostType::ComputeSha256Hash, Some(buf.len() as u64))?;
Ok(Sha256::digest(&buf).try_into()?)
}

pub(crate) fn metered_from_xdr<T: ReadXdr>(&self, bytes: &[u8]) -> Result<T, HostError> {
pub fn metered_from_xdr<T: ReadXdr>(&self, bytes: &[u8]) -> Result<T, HostError> {
let _span = tracy_span!("read xdr");
self.charge_budget(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
self.map_err(T::from_xdr(bytes))
Expand All @@ -68,3 +54,30 @@ impl Host {
self.visit_obj(bytes, |hv: &ScBytes| self.metered_from_xdr(hv.as_slice()))
}
}

pub fn metered_write_xdr(
budget: &Budget,
obj: &impl WriteXdr,
w: &mut Vec<u8>,
) -> Result<(), HostError> {
let _span = tracy_span!("write xdr");
let mw = MeteredWrite { budget, w };
let mut w = DepthLimitedWrite::new(mw, DEFAULT_XDR_RW_DEPTH_LIMIT);
// MeteredWrite above turned any budget failure into an IO error; we turn it
// back to a budget failure here, since there's really no "IO error" that can
// occur when writing to a Vec<u8>.
obj.write_xdr(&mut w)
.map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
}

// Host-less metered XDR decoding.
// Prefer using `metered_from_xdr` when host is available for better error
// reporting.
pub fn metered_from_xdr_with_budget<T: ReadXdr>(
bytes: &[u8],
budget: &Budget,
) -> Result<T, HostError> {
let _span = tracy_span!("read xdr with budget");
budget.charge(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
T::from_xdr(bytes).map_err(|e| e.into())
}
3 changes: 2 additions & 1 deletion soroban-env-host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
//! - The [storage] module which is responsible for providing an interface
//! between contracts and their durable storage.
//!

#![recursion_limit = "256"]
#[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
macro_rules! tracy_span {
() => {
Expand Down Expand Up @@ -71,4 +71,5 @@ pub use host::{
};
pub use soroban_env_common::*;

pub mod e2e_invoke;
pub mod fees;
6 changes: 3 additions & 3 deletions soroban-env-host/src/test/budget_metering.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
budget::AsBudget,
host::metered_clone::MeteredClone,
host::{metered_clone::MeteredClone, metered_xdr::metered_write_xdr},
xdr::{ContractCostType, ScMap, ScMapEntry, ScVal},
Env, Host, HostError, Symbol, Val,
};
Expand Down Expand Up @@ -93,7 +93,7 @@ fn metered_xdr() -> Result<(), HostError> {
.try_into(),
)?;
let mut w = Vec::<u8>::new();
host.metered_write_xdr(&scmap, &mut w)?;
metered_write_xdr(host.budget_ref(), &scmap, &mut w)?;
host.with_budget(|budget| {
assert_eq!(
budget.get_tracker(ContractCostType::ValSer)?.1,
Expand Down Expand Up @@ -133,7 +133,7 @@ fn metered_xdr_out_of_budget() -> Result<(), HostError> {
.try_into(),
)?;
let mut w = Vec::<u8>::new();
let res = host.metered_write_xdr(&scmap, &mut w);
let res = metered_write_xdr(host.budget_ref(), &scmap, &mut w);
let code = (ScErrorType::Budget, ScErrorCode::ExceededLimit);
assert!(HostError::result_matches_err(res, code));
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-host/src/test/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn run_complex() -> Result<(), HostError> {
Symbol::try_from_small_str("go")?,
host.add_host_object(HostVec::new())?,
)?;
let (store, _, _) = host.try_finish().unwrap();
let (store, _) = host.try_finish().unwrap();
store.footprint
};

Expand Down

0 comments on commit 4466087

Please sign in to comment.