Skip to content

Commit

Permalink
Add host function for bumping contract instance (#875)
Browse files Browse the repository at this point in the history
* Add host function for bumping contract instance

* Use `MeteredVector` for `LedgerBump`; plus some cleanups

* Bump meta and update test wasm

* Reverse using `MeteredVector`. Use regular `Vec` and charge for it.

* fixup! Reverse using `MeteredVector`. Use regular `Vec` and charge for it.

* Use `Rc::Clone`
  • Loading branch information
jayz22 authored Jun 23, 2023
1 parent e47a25e commit 035af74
Show file tree
Hide file tree
Showing 30 changed files with 136 additions and 56 deletions.
14 changes: 13 additions & 1 deletion soroban-env-common/env.json
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,18 @@
},
{
"export": "8",
"name": "bump_current_contract_instance_and_code",
"args": [
{
"name": "min",
"type": "U32Val"
}
],
"return": "Void",
"docs": "Bumps the expiration ledger the current contract instance and code (if applicable), so they will live for `min` ledgers from now. If the current expiration ledger is already large enough (>= last closed ledger + `min` more ledgers), it is untouched."
},
{
"export": "9",
"name": "get_contract_id",
"args": [
{
Expand All @@ -1406,7 +1418,7 @@
"docs": "Get the id of a contract without creating it. `deployer` is address of the contract deployer. `salt` is used to create a unique contract id. Returns the address of the would-be contract."
},
{
"export": "9",
"export": "10",
"name": "get_asset_contract_id",
"args": [
{
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-common/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub const ENV_META_V0_SECTION_NAME: &str = "contractenvmetav0";

soroban_env_macros::generate_env_meta_consts!(
ledger_protocol_version: 20,
pre_release_version: 48,
pre_release_version: 49,
);

pub fn get_ledger_protocol_version(interface_version: u64) -> u32 {
Expand Down
2 changes: 1 addition & 1 deletion soroban-env-host/benches/common/cost_types/vec_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl HostCostMeasurement for VecEntryMeasure {
fn new_random_case(_host: &Host, rng: &mut StdRng, input: u64) -> VecEntrySample {
let input = 1 + input * Self::STEP_SIZE;
let ov = util::to_rawval_u32(0..(input as u32)).collect();
let vec: MeteredVector<_> = MeteredVector::from_vec(ov).unwrap();
let vec: MeteredVector<_> = MeteredVector::from_vec(ov);
let mut idxs: Vec<usize> = (0..input as usize).collect();
idxs.shuffle(rng);
VecEntrySample { vec, idxs }
Expand Down
6 changes: 6 additions & 0 deletions soroban-env-host/src/budget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,12 @@ impl AsBudget for Host {
}
}

impl AsBudget for &Host {
fn as_budget(&self) -> &Budget {
self.budget_ref()
}
}

impl Budget {
/// Initializes the budget from network configuration settings.
pub fn from_configs(
Expand Down
22 changes: 20 additions & 2 deletions soroban-env-host/src/expiration_ledger_bumps.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::rc::Rc;

use crate::xdr::LedgerKey;
use crate::{budget::AsBudget, host::metered_clone, xdr::LedgerKey, HostError};

#[derive(Clone)]
pub struct LedgerBump {
Expand All @@ -9,4 +9,22 @@ pub struct LedgerBump {
}

#[derive(Clone, Default)]
pub struct ExpirationLedgerBumps(pub Vec<LedgerBump>);
pub struct ExpirationLedgerBumps(Vec<LedgerBump>);

impl ExpirationLedgerBumps {
pub(crate) fn metered_push(
&mut self,
b: impl AsBudget,
elem: LedgerBump,
) -> Result<(), HostError> {
// The worst case cost of a `vec.push` requires heap reallocation and copying of old data.
// Thus we use the cost of instantiating a size=1 `Vec` as the estimate of the amortized
// cost for it.
metered_clone::charge_container_bulk_init_with_elts::<Vec<LedgerBump>, LedgerBump>(
1,
b.as_budget(),
)?;
self.0.push(elem);
Ok(())
}
}
56 changes: 48 additions & 8 deletions soroban-env-host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ impl VmCallerEnv for Host {
impl_bignum_host_fns_rhs_u32!(i256_shr, checked_shr, I256, Int256Shift);

fn map_new(&self, _vmcaller: &mut VmCaller<Host>) -> Result<MapObject, HostError> {
self.add_host_object(HostMap::new()?)
self.add_host_object(HostMap::new())
}

fn map_put(
Expand Down Expand Up @@ -1507,7 +1507,7 @@ impl VmCallerEnv for Host {
self.usize_from_rawval_u32_input("c", c)?
};
// TODO: optimize the vector based on capacity
self.add_host_object(HostVec::new()?)
self.add_host_object(HostVec::new())
}

fn vec_put(
Expand Down Expand Up @@ -1718,7 +1718,7 @@ impl VmCallerEnv for Host {
vals.as_mut_slice(),
|buf| Val::from_payload(u64::from_le_bytes(*buf)),
)?;
self.add_host_object(HostVec::from_vec(vals)?)
self.add_host_object(HostVec::from_vec(vals))
}

fn vec_unpack_to_linear_memory(
Expand Down Expand Up @@ -1895,10 +1895,50 @@ impl VmCallerEnv for Host {

let min_expiration =
self.with_ledger_info(|li| Ok(li.sequence_number.saturating_add(min.into())))?;
self.0.expiration_bumps.borrow_mut().0.push(LedgerBump {
key,
min_expiration,
});
self.0.expiration_bumps.borrow_mut().metered_push(
self,
LedgerBump {
key,
min_expiration,
},
)?;
Ok(Val::VOID)
}

fn bump_current_contract_instance_and_code(
&self,
_vmcaller: &mut VmCaller<Host>,
min: U32Val,
) -> Result<Void, HostError> {
let min_expiration =
self.with_ledger_info(|li| Ok(li.sequence_number.saturating_add(min.into())))?;

let contract_id = self.get_current_contract_id_internal()?;
let key = self.contract_instance_ledger_key(&contract_id)?;
self.0.expiration_bumps.borrow_mut().metered_push(
self,
LedgerBump {
key: Rc::clone(&key),
min_expiration,
},
)?;

match self
.retrieve_contract_instance_from_storage(&key)?
.executable
{
ContractExecutable::Wasm(wasm_hash) => {
let key = self.contract_code_ledger_key(&wasm_hash)?;
self.0.expiration_bumps.borrow_mut().metered_push(
self,
LedgerBump {
key,
min_expiration,
},
)?;
}
ContractExecutable::Token => {}
}
Ok(Val::VOID)
}

Expand Down Expand Up @@ -2559,7 +2599,7 @@ impl VmCallerEnv for Host {
let inner = MeteredVector::from_array(&vals, self.as_budget())?;
outer.push(self.add_host_object(inner)?.into());
}
self.add_host_object(HostVec::from_vec(outer)?)
self.add_host_object(HostVec::from_vec(outer))
}

fn fail_with_error(
Expand Down
24 changes: 5 additions & 19 deletions soroban-env-host/src/host/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,6 @@ impl Host {
self.storage_key_from_scval(self.from_host_val(k)?, durability)
}

pub(crate) fn storage_key_for_contract(
&self,
contract_id: Hash,
key: ScVal,
durability: ContractDataDurability,
) -> Rc<LedgerKey> {
self.storage_key_for_address(ScAddress::Contract(contract_id), key, durability)
}

pub(crate) fn storage_key_for_address(
&self,
contract_address: ScAddress,
Expand All @@ -183,22 +174,17 @@ impl Host {
}))
}

pub fn storage_key_from_scval(
pub(crate) fn storage_key_from_scval(
&self,
key: ScVal,
durability: ContractDataDurability,
) -> Result<Rc<LedgerKey>, HostError> {
Ok(
self.storage_key_for_contract(
self.get_current_contract_id_internal()?,
key,
durability,
),
)
let contract_id = self.get_current_contract_id_internal()?;
Ok(self.storage_key_for_address(ScAddress::Contract(contract_id), key, durability))
}

// Notes on metering: covered by components.
pub fn contract_data_key_from_rawval(
pub(crate) fn contract_data_key_from_rawval(
&self,
k: Val,
durability: ContractDataDurability,
Expand Down Expand Up @@ -499,7 +485,7 @@ impl Host {
for e in v.iter() {
vv.push(self.to_host_val(e)?)
}
Ok(self.add_host_object(HostVec::from_vec(vv)?)?.into())
Ok(self.add_host_object(HostVec::from_vec(vv))?.into())
}
ScVal::Map(Some(m)) => {
metered_clone::charge_heap_alloc::<(Val, Val)>(m.len() as u64, self.as_budget())?;
Expand Down
9 changes: 6 additions & 3 deletions soroban-env-host/src/host/data_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ impl Host {
}
}

pub(crate) fn wasm_ledger_key(&self, wasm_hash: &Hash) -> Result<Rc<LedgerKey>, HostError> {
pub(crate) fn contract_code_ledger_key(
&self,
wasm_hash: &Hash,
) -> Result<Rc<LedgerKey>, HostError> {
let wasm_hash = wasm_hash.metered_clone(self.as_budget())?;
Ok(Rc::new(LedgerKey::ContractCode(LedgerKeyContractCode {
hash: wasm_hash,
Expand All @@ -75,7 +78,7 @@ impl Host {
}

pub(crate) fn retrieve_wasm_from_storage(&self, wasm_hash: &Hash) -> Result<BytesM, HostError> {
let key = self.wasm_ledger_key(wasm_hash)?;
let key = self.contract_code_ledger_key(wasm_hash)?;
match &self
.0
.storage
Expand All @@ -101,7 +104,7 @@ impl Host {
}

pub(crate) fn wasm_exists(&self, wasm_hash: &Hash) -> Result<bool, HostError> {
let key = self.wasm_ledger_key(wasm_hash)?;
let key = self.contract_code_ledger_key(wasm_hash)?;
self.0.storage.borrow_mut().has(&key, self.as_budget())
}

Expand Down
4 changes: 4 additions & 0 deletions soroban-env-host/src/host/declared_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::rc::Rc;

use crate::{
events::{EventError, HostEvent, InternalContractEvent, InternalEvent},
expiration_ledger_bumps::LedgerBump,
host::Events,
host_object::HostObject,
storage::AccessType,
Expand Down Expand Up @@ -95,6 +96,7 @@ impl_declared_size_type!(SymbolSmallIter, 8);
impl_declared_size_type!(U256, 32);
impl_declared_size_type!(I256, 32);
impl_declared_size_type!(HostObject, 48);
impl_declared_size_type!(LedgerBump, 20);
// xdr types
impl_declared_size_type!(TimePoint, 8);
impl_declared_size_type!(Duration, 8);
Expand Down Expand Up @@ -272,6 +274,7 @@ mod test {
expect!["40"].assert_eq(size_of::<HostObject>().to_string().as_str());
#[cfg(target_arch = "aarch64")]
expect!["48"].assert_eq(size_of::<HostObject>().to_string().as_str());
expect!["20"].assert_eq(size_of::<LedgerBump>().to_string().as_str());
// xdr types
expect!["8"].assert_eq(size_of::<TimePoint>().to_string().as_str());
expect!["8"].assert_eq(size_of::<Duration>().to_string().as_str());
Expand Down Expand Up @@ -411,6 +414,7 @@ mod test {
assert_mem_size_le_declared_size!(U256);
assert_mem_size_le_declared_size!(I256);
assert_mem_size_le_declared_size!(HostObject);
assert_mem_size_le_declared_size!(LedgerBump);
// xdr types
assert_mem_size_le_declared_size!(TimePoint);
assert_mem_size_le_declared_size!(Duration);
Expand Down
2 changes: 2 additions & 0 deletions soroban-env-host/src/host/metered_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use soroban_env_common::xdr::ScContractInstance;
use crate::{
budget::Budget,
events::{EventError, HostEvent, InternalContractEvent, InternalEvent},
expiration_ledger_bumps::LedgerBump,
host::Events,
host_object::HostObject,
storage::AccessType,
Expand Down Expand Up @@ -193,6 +194,7 @@ impl MeteredClone for SymbolSmallIter {}
impl MeteredClone for U256 {}
impl MeteredClone for I256 {}
impl MeteredClone for HostObject {}
impl MeteredClone for LedgerBump {}
// xdr types
impl MeteredClone for TimePoint {}
impl MeteredClone for Duration {}
Expand Down
6 changes: 3 additions & 3 deletions soroban-env-host/src/host/metered_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ where
V: MeteredClone,
Ctx: AsBudget + Compare<K, Error = HostError>,
{
pub fn new() -> Result<Self, HostError> {
Ok(MeteredOrdMap {
pub fn new() -> Self {
MeteredOrdMap {
map: Vec::new(),
ctx: Default::default(),
})
}
}

pub fn from_map(map: Vec<(K, V)>, ctx: &Ctx) -> Result<Self, HostError> {
Expand Down
25 changes: 17 additions & 8 deletions soroban-env-host/src/host/metered_vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ use std::{cmp::Ordering, ops::Range};

const VEC_OOB: Error = Error::from_type_and_code(ScErrorType::Object, ScErrorCode::IndexBounds);

#[derive(Clone, Default)]
#[derive(Clone)]
pub struct MeteredVector<A> {
vec: Vec<A>,
}

impl<A> Default for MeteredVector<A> {
fn default() -> Self {
Self {
vec: Default::default(),
}
}
}

impl<A> MeteredVector<A>
where
A: DeclaredSizeForMetering,
Expand All @@ -40,7 +48,8 @@ impl<A> MeteredVector<A>
where
A: MeteredClone,
{
pub fn new() -> Result<Self, HostError> {
// Constructs a empty new `MeteredVector`.
pub fn new() -> Self {
Self::from_vec(Vec::new())
}

Expand All @@ -51,19 +60,19 @@ where
#[cfg(any(test, feature = "testutils"))]
pub fn with_capacity(capacity: usize, budget: &Budget) -> Result<Self, HostError> {
super::metered_clone::charge_heap_alloc::<A>(capacity as u64, budget)?;
Self::from_vec(Vec::with_capacity(capacity))
Ok(Self::from_vec(Vec::with_capacity(capacity)))
}

pub fn from_array(buf: &[A], budget: &Budget) -> Result<Self, HostError> {
// we may temporarily go over budget here.
let vec: Vec<A> = buf.into();
vec.charge_deep_clone(budget)?;
Self::from_vec(vec)
Ok(Self::from_vec(vec))
}

// No meter charge, assuming allocation cost has been covered by the caller from the outside.
pub fn from_vec(vec: Vec<A>) -> Result<Self, HostError> {
Ok(Self { vec })
pub fn from_vec(vec: Vec<A>) -> Self {
Self { vec }
}

pub fn as_slice(&self) -> &[A] {
Expand All @@ -89,7 +98,7 @@ where
// the clone into one (when A::IS_SHALLOW==true).
let vec: Vec<A> = iter.collect();
vec.charge_deep_clone(budget)?;
Self::from_vec(vec)
Ok(Self::from_vec(vec))
} else {
// This is a logic error, we should never get here.
Err((ScErrorType::Object, ScErrorCode::InternalError).into())
Expand Down Expand Up @@ -280,7 +289,7 @@ where
}
}
vec.charge_deep_clone(budget)?;
Self::from_vec(vec)
Ok(Self::from_vec(vec))
}

pub fn iter(&self) -> std::slice::Iter<'_, A> {
Expand Down
Loading

0 comments on commit 035af74

Please sign in to comment.