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

Add helpers for container bulk init; applies to auth metering #975

Merged
merged 3 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
217 changes: 160 additions & 57 deletions soroban-env-host/src/auth.rs

Large diffs are not rendered by default.

11 changes: 4 additions & 7 deletions soroban-env-host/src/e2e_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ use soroban_env_common::{
};

use crate::{
budget::Budget,
budget::{AsBudget, Budget},
events::Events,
fees::LedgerEntryRentChange,
host::{
ledger_info_helper::{get_entry_expiration, get_key_durability, set_entry_expiration},
metered_clone::{charge_container_bulk_init_with_elts, MeteredClone},
metered_clone::{MeteredClone, MeteredContainer},
metered_xdr::{metered_from_xdr_with_budget, metered_write_xdr},
},
storage::{AccessType, Footprint, FootprintMap, SnapshotSource, Storage, StorageMap},
Expand Down Expand Up @@ -450,12 +450,9 @@ impl Host {
&self,
encoded_contract_auth_entries: I,
) -> Result<Vec<SorobanAuthorizationEntry>, HostError> {
charge_container_bulk_init_with_elts::<
Vec<SorobanAuthorizationEntry>,
SorobanAuthorizationEntry,
>(
Vec::<SorobanAuthorizationEntry>::charge_bulk_init(
encoded_contract_auth_entries.len() as u64,
self.budget_ref(),
self.as_budget(),
)?;
encoded_contract_auth_entries
.map(|buf| self.metered_from_xdr::<SorobanAuthorizationEntry>(buf.as_ref()))
Expand Down
60 changes: 27 additions & 33 deletions soroban-env-host/src/events/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use soroban_env_common::{BytesObject, VecObject};
use super::{Events, HostEvent};
use crate::{
budget::{AsBudget, Budget},
host::metered_clone,
host::metered_clone::MeteredContainer,
xdr,
xdr::ScVal,
Host, HostError, Val,
Expand Down Expand Up @@ -134,13 +134,10 @@ impl InternalEventsBuffer {
// `Vec.push(event)`. Because the buffer length may be different on different instances
// due to diagnostic events and we need a deterministic cost across all instances,
// the cost needs to be amortized and buffer size-independent.

if let InternalEvent::Contract(_) = e {
metered_clone::charge_container_bulk_init_with_elts::<
Vec<(InternalEvent, EventError)>,
(InternalEvent, EventError),
>(1, budget)?;
Vec::<(InternalEvent, EventError)>::charge_bulk_init(1, budget)?;
}

self.vec.push((e, EventError::FromSuccessfulCall));
Ok(())
}
Expand Down Expand Up @@ -172,33 +169,30 @@ impl InternalEventsBuffer {
/// Converts the internal events into their external representation. This should only be called
/// either when the host is finished (via `try_finish`), or when an error occurs.
pub fn externalize(&self, host: &Host) -> Result<Events, HostError> {
let vec: Result<Vec<HostEvent>, HostError> =
self.vec
.iter()
.map(|e| match &e.0 {
InternalEvent::Contract(c) => {
// Metering: we use the cost of instantiating a size=1 `Vec` as an estimate
// for the cost collecting 1 `HostEvent` into the events buffer. Because
// the resulting buffer length may be different on different instances
// (due to diagnostic events) and we need a deterministic cost across all
// instances, the cost needs to be amortized and buffer size-independent.
metered_clone::charge_container_bulk_init_with_elts::<
Vec<HostEvent>,
HostEvent,
>(1, host.as_budget())?;
Ok(HostEvent {
event: c.to_xdr(host)?,
failed_call: e.1 == EventError::FromFailedCall,
})
}
InternalEvent::Diagnostic(c) => host.as_budget().with_free_budget(|| {
Ok(HostEvent {
event: c.to_xdr(host)?,
failed_call: e.1 == EventError::FromFailedCall,
})
}),
})
.collect();
let vec: Result<Vec<HostEvent>, HostError> = self
.vec
.iter()
.map(|e| match &e.0 {
InternalEvent::Contract(c) => {
// Metering: we use the cost of instantiating a size=1 `Vec` as an estimate
// for the cost collecting 1 `HostEvent` into the events buffer. Because
// the resulting buffer length may be different on different instances
// (due to diagnostic events) and we need a deterministic cost across all
// instances, the cost needs to be amortized and buffer size-independent.
Vec::<HostEvent>::charge_bulk_init(1, host.as_budget())?;
Ok(HostEvent {
event: c.to_xdr(host)?,
failed_call: e.1 == EventError::FromFailedCall,
})
}
InternalEvent::Diagnostic(c) => host.as_budget().with_free_budget(|| {
Ok(HostEvent {
event: c.to_xdr(host)?,
failed_call: e.1 == EventError::FromFailedCall,
})
}),
})
.collect();
Ok(Events(vec?))
}
}
42 changes: 17 additions & 25 deletions soroban-env-host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ use self::{
metered_vector::MeteredVector,
prng::Prng,
};
use self::{metered_clone::MeteredClone, metered_xdr::metered_write_xdr};
use self::{
metered_clone::{MeteredClone, MeteredContainer},
metered_xdr::metered_write_xdr,
};
use crate::impl_bignum_host_fns;
use crate::Compare;
#[cfg(any(test, feature = "testutils"))]
Expand Down Expand Up @@ -1017,21 +1020,22 @@ impl EnvBase for Host {
}

fn string_new_from_slice(&self, s: &str) -> Result<StringObject, HostError> {
self.add_host_object(ScString(s.as_bytes().to_vec().try_into()?))
self.add_host_object(ScString(
self.metered_slice_to_vec(s.as_bytes())?.try_into()?,
))
}

fn symbol_new_from_slice(&self, s: &str) -> Result<SymbolObject, HostError> {
for ch in s.chars() {
SymbolSmall::validate_char(ch)?;
}
self.add_host_object(ScSymbol(s.as_bytes().to_vec().try_into()?))
self.add_host_object(ScSymbol(
self.metered_slice_to_vec(s.as_bytes())?.try_into()?,
))
}

fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, HostError> {
metered_clone::charge_container_bulk_init_with_elts::<Vec<Symbol>, Symbol>(
keys.len() as u64,
self.as_budget(),
)?;
Vec::<Symbol>::charge_bulk_init(keys.len() as u64, self.as_budget())?;
// If only fallible iterators worked better in Rust, we would not need this Vec<...>.
let mut key_syms: Vec<Symbol> = Vec::with_capacity(keys.len());
for k in keys.iter() {
Expand Down Expand Up @@ -1670,11 +1674,7 @@ impl VmCallerEnv for Host {
pos: keys_pos,
len,
} = self.decode_vmslice(keys_pos, len)?;
// covers `Vec::with_capacity` and `len` pushes
metered_clone::charge_container_bulk_init_with_elts::<Vec<Symbol>, Symbol>(
len as u64,
self.as_budget(),
)?;
Vec::<Symbol>::charge_bulk_init(len as u64, self.as_budget())?;
let mut key_syms: Vec<Symbol> = Vec::with_capacity(len as usize);
self.metered_vm_scan_slices_in_linear_memory(
vmcaller,
Expand All @@ -1692,10 +1692,7 @@ impl VmCallerEnv for Host {

// Step 2: extract all val Vals.
let vals_pos: u32 = vals_pos.into();
metered_clone::charge_container_bulk_init_with_elts::<Vec<Symbol>, Symbol>(
len as u64,
self.as_budget(),
)?;
Vec::<Val>::charge_bulk_init(len as u64, self.as_budget())?;
let mut vals: Vec<Val> = vec![Val::VOID.into(); len as usize];
self.metered_vm_read_vals_from_linear_memory::<8, Val>(
vmcaller,
Expand Down Expand Up @@ -1986,10 +1983,7 @@ impl VmCallerEnv for Host {
len: U32Val,
) -> Result<VecObject, HostError> {
let VmSlice { vm, pos, len } = self.decode_vmslice(vals_pos, len)?;
metered_clone::charge_container_bulk_init_with_elts::<Vec<Symbol>, Symbol>(
len as u64,
self.as_budget(),
)?;
Vec::<Val>::charge_bulk_init(len as u64, self.as_budget())?;
let mut vals: Vec<Val> = vec![Val::VOID.to_val(); len as usize];
self.metered_vm_read_vals_from_linear_memory::<8, Val>(
vmcaller,
Expand Down Expand Up @@ -2767,9 +2761,7 @@ impl VmCallerEnv for Host {
let end: u32 = end.into();
let vnew = self.visit_obj(b, move |hv: &ScBytes| {
let range = self.valid_range_from_start_end_bound(start, end, hv.len())?;
metered_clone::charge_heap_alloc::<u8>(range.len() as u64, self.as_budget())?;
metered_clone::charge_shallow_copy::<u8>(range.len() as u64, self.as_budget())?;
Ok(hv.as_slice()[range].to_vec())
self.metered_slice_to_vec(&hv.as_slice()[range])
})?;
self.add_host_object(self.scbytes_from_vec(vnew)?)
}
Expand Down Expand Up @@ -2979,7 +2971,7 @@ impl VmCallerEnv for Host {
let addr = self.visit_obj(address, |addr: &ScAddress| Ok(addr.clone()))?;
match addr {
ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(pk))) => Ok(self
.add_host_object(ScBytes(pk.0.to_vec().try_into()?))?
.add_host_object(ScBytes(self.metered_slice_to_vec(&pk.0)?.try_into()?))?
.into()),
ScAddress::Contract(_) => Ok(().into()),
}
Expand All @@ -2994,7 +2986,7 @@ impl VmCallerEnv for Host {
match addr {
ScAddress::Account(_) => Ok(().into()),
ScAddress::Contract(Hash(h)) => Ok(self
.add_host_object(ScBytes(h.to_vec().try_into()?))?
.add_host_object(ScBytes(self.metered_slice_to_vec(&h)?.try_into()?))?
.into()),
}
}
Expand Down
28 changes: 14 additions & 14 deletions soroban-env-host/src/host/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::rc::Rc;

use super::metered_clone::{self, charge_container_bulk_init_with_elts, MeteredClone};
use super::metered_clone::{self, MeteredClone, MeteredCollect, MeteredContainer};
use crate::budget::AsBudget;
use crate::err;
use crate::host_object::{HostMap, HostObject, HostVec};
Expand Down Expand Up @@ -242,14 +242,10 @@ impl Host {
}

pub(crate) fn rawvals_to_sc_val_vec(&self, raw_vals: &[Val]) -> Result<VecM<ScVal>, HostError> {
charge_container_bulk_init_with_elts::<Vec<Val>, Val>(
raw_vals.len() as u64,
self.as_budget(),
)?;
raw_vals
.iter()
.map(|v| self.from_host_val(*v))
.collect::<Result<Vec<ScVal>, HostError>>()?
.metered_collect::<Result<Vec<ScVal>, HostError>>(self.as_budget())??
.try_into()
.map_err(|_| {
err!(
Expand Down Expand Up @@ -281,21 +277,19 @@ impl Host {
}

pub(crate) fn scvals_to_rawvals(&self, sc_vals: &[ScVal]) -> Result<Vec<Val>, HostError> {
charge_container_bulk_init_with_elts::<Vec<Val>, Val>(
sc_vals.len() as u64,
self.as_budget(),
)?;
sc_vals
.iter()
.map(|scv| self.to_host_val(scv))
.collect::<Result<Vec<Val>, HostError>>()
.metered_collect::<Result<Vec<Val>, HostError>>(self.as_budget())?
}

pub(crate) fn bytesobj_from_internal_contract_id(
&self,
) -> Result<Option<BytesObject>, HostError> {
if let Some(id) = self.get_current_contract_id_opt_internal()? {
let obj = self.add_host_object::<ScBytes>(id.as_slice().to_vec().try_into()?)?;
let obj = self.add_host_object::<ScBytes>(
self.metered_slice_to_vec(id.as_slice())?.try_into()?,
)?;
Ok(Some(obj))
} else {
Ok(None)
Expand All @@ -306,8 +300,14 @@ impl Host {
Ok(ScBytes(v.try_into()?))
}

pub(crate) fn scbytes_from_slice(&self, slice: &[u8]) -> Result<ScBytes, HostError> {
self.scbytes_from_vec(slice.to_vec())
pub(crate) fn metered_slice_to_vec(&self, s: &[u8]) -> Result<Vec<u8>, HostError> {
Vec::<u8>::charge_bulk_init(s.len() as u64, self.as_budget())?;
Ok(s.to_vec())
}

// metering: covered
pub(crate) fn scbytes_from_slice(&self, s: &[u8]) -> Result<ScBytes, HostError> {
self.scbytes_from_vec(self.metered_slice_to_vec(s)?)
}

pub(crate) fn scbytes_from_hash(&self, hash: &Hash) -> Result<ScBytes, HostError> {
Expand Down
Loading