diff --git a/soroban-env-common/env.json b/soroban-env-common/env.json index 1466a72ee..49e68fee5 100644 --- a/soroban-env-common/env.json +++ b/soroban-env-common/env.json @@ -804,7 +804,7 @@ "type": "RawVal" } ], - "return": "RawVal" + "return": "Void" }, { "export": "0", @@ -837,7 +837,7 @@ "type": "RawVal" } ], - "return": "RawVal" + "return": "Void" }, { "export": "3", @@ -854,6 +854,54 @@ ], "return": "BytesObject", "docs": "Deploys a contract from the current contract. `wasm_hash` must be a hash of the contract code that has already been installed on this network. `salt` is used to create a unique contract id." + }, + { + "export": "4", + "name": "put_tmp_contract_data", + "args": [ + { + "name": "k", + "type": "RawVal" + }, + { + "name": "v", + "type": "RawVal" + } + ], + "return": "Void" + }, + { + "export": "5", + "name": "has_tmp_contract_data", + "args": [ + { + "name": "k", + "type": "RawVal" + } + ], + "return": "Bool" + }, + { + "export": "6", + "name": "get_tmp_contract_data", + "args": [ + { + "name": "k", + "type": "RawVal" + } + ], + "return": "RawVal" + }, + { + "export": "7", + "name": "del_tmp_contract_data", + "args": [ + { + "name": "k", + "type": "RawVal" + } + ], + "return": "Void" } ] }, diff --git a/soroban-env-common/src/meta.rs b/soroban-env-common/src/meta.rs index 1a03fac6b..2a382ed8a 100644 --- a/soroban-env-common/src/meta.rs +++ b/soroban-env-common/src/meta.rs @@ -58,5 +58,5 @@ pub const ENV_META_V0_SECTION_NAME: &'static str = "contractenvmetav0"; soroban_env_macros::generate_env_meta_consts!( - interface_version: 32, + interface_version: 33, ); diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index e87616048..078a2b081 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -20,7 +20,6 @@ use soroban_env_common::{ U128Object, U32Val, U64Object, U64Val, VecObject, VmCaller, VmCallerEnv, Void, I256, U256, }; -use crate::budget::{AsBudget, Budget, CostType}; use crate::events::{ DebugError, DebugEvent, Events, InternalContractEvent, InternalEvent, InternalEventsBuffer, }; @@ -29,6 +28,10 @@ use crate::{ auth::{AuthorizationManager, AuthorizationManagerSnapshot, RecordedAuthPayload}, native_contract::account_contract::ACCOUNT_CONTRACT_CHECK_AUTH_FN_NAME, }; +use crate::{ + budget::{AsBudget, Budget, CostType}, + storage::{TempStorage, TempStorageMap}, +}; use crate::host_object::{HostMap, HostObject, HostObjectType, HostVec}; use crate::SymbolStr; @@ -62,6 +65,7 @@ use crate::Compare; #[derive(Clone)] struct RollbackPoint { storage: StorageMap, + temp_storage: TempStorageMap, events: usize, auth: Option, } @@ -160,6 +164,7 @@ pub(crate) struct HostImpl { ledger: RefCell>, objects: RefCell>, storage: RefCell, + temp_storage: RefCell, pub(crate) context: RefCell>, // Note: budget is refcounted and is _not_ deep-cloned when you call HostImpl::deep_clone, // mainly because it's not really possible to achieve (the same budget is connected to many @@ -238,6 +243,7 @@ impl Host { ledger: RefCell::new(None), objects: Default::default(), storage: RefCell::new(storage), + temp_storage: Default::default(), context: Default::default(), budget: budget.clone(), events: Default::default(), @@ -431,6 +437,7 @@ impl Host { self.0.context.borrow_mut().push(frame); Ok(RollbackPoint { storage: self.0.storage.borrow().map.clone(), + temp_storage: self.0.temp_storage.borrow().map.clone(), events: self.0.events.borrow().vec.len(), auth: auth_snapshot, }) @@ -2303,7 +2310,7 @@ impl VmCallerEnv for Host { _vmcaller: &mut VmCaller, k: RawVal, v: RawVal, - ) -> Result { + ) -> Result { let key = self.contract_data_key_from_rawval(k)?; let data = LedgerEntryData::ContractData(ContractDataEntry { contract_id: self.get_current_contract_id_internal()?, @@ -2315,7 +2322,7 @@ impl VmCallerEnv for Host { &Host::ledger_entry_from_data(data), self.as_budget(), )?; - Ok(().into()) + Ok(RawVal::VOID) } // Notes on metering: covered by components @@ -2355,10 +2362,10 @@ impl VmCallerEnv for Host { &self, _vmcaller: &mut VmCaller, k: RawVal, - ) -> Result { + ) -> Result { let key = self.contract_data_key_from_rawval(k)?; self.0.storage.borrow_mut().del(&key, self.as_budget())?; - Ok(().into()) + Ok(RawVal::VOID) } // Notes on metering: covered by the components. @@ -2377,6 +2384,67 @@ impl VmCallerEnv for Host { self.create_contract_with_id_preimage(code, id_preimage) } + // Notes on metering: covered by components + fn put_tmp_contract_data( + &self, + _vmcaller: &mut VmCaller, + k: RawVal, + v: RawVal, + ) -> Result { + let key = self.from_host_val(k)?; + self.0.temp_storage.borrow_mut().put( + self.get_current_contract_id_internal()?.0, + key, + v, + self.as_budget(), + )?; + Ok(RawVal::VOID) + } + + // Notes on metering: covered by components + fn has_tmp_contract_data( + &self, + _vmcaller: &mut VmCaller, + k: RawVal, + ) -> Result { + let key = self.from_host_val(k)?; + let res = self.0.temp_storage.borrow_mut().has( + self.get_current_contract_id_internal()?.0, + key, + self.as_budget(), + )?; + Ok(RawVal::from_bool(res)) + } + + // Notes on metering: covered by components + fn get_tmp_contract_data( + &self, + _vmcaller: &mut VmCaller, + k: RawVal, + ) -> Result { + let key = self.from_host_val(k)?; + self.0.temp_storage.borrow_mut().get( + self.get_current_contract_id_internal()?.0, + key, + self.as_budget(), + ) + } + + // Notes on metering: covered by components + fn del_tmp_contract_data( + &self, + _vmcaller: &mut VmCaller, + k: RawVal, + ) -> Result { + let key = self.from_host_val(k)?; + self.0.temp_storage.borrow_mut().del( + self.get_current_contract_id_internal()?.0, + key, + self.as_budget(), + )?; + Ok(RawVal::VOID) + } + // Notes on metering: here covers the args unpacking. The actual VM work is changed at lower layers. fn call( &self, diff --git a/soroban-env-host/src/storage.rs b/soroban-env-host/src/storage.rs index 20a46f146..aae94e8e7 100644 --- a/soroban-env-host/src/storage.rs +++ b/soroban-env-host/src/storage.rs @@ -7,17 +7,24 @@ //! - [Env::put_contract_data](crate::Env::put_contract_data) //! - [Env::del_contract_data](crate::Env::del_contract_data) +use std::cmp::Ordering; use std::rc::Rc; -use soroban_env_common::Compare; +use soroban_env_common::{Compare, RawVal}; use crate::budget::Budget; -use crate::xdr::{LedgerEntry, LedgerKey, ScHostStorageErrorCode}; +use crate::xdr::{LedgerEntry, LedgerKey, ScHostStorageErrorCode, ScVal}; use crate::Host; use crate::{host::metered_map::MeteredOrdMap, HostError}; pub type FootprintMap = MeteredOrdMap, AccessType, Budget>; pub type StorageMap = MeteredOrdMap, Option>, Budget>; +// Alias for the `RawVal` that has the cheap 'shallow' comparison defined - it +// just compares the payloads without looking at the value semantics. +// This is used in `TempStorageMap` to make sure that we don't do expensive +// value comparisons. +pub type ShallowComparableRawVal = RawVal; +pub type TempStorageMap = MeteredOrdMap<([u8; 32], ScVal), ShallowComparableRawVal, Budget>; /// A helper type used by [Footprint] to designate which ways /// a given [LedgerKey] is accessed, or is allowed to be accessed, @@ -294,6 +301,81 @@ impl Storage { } } +impl Compare for Budget { + type Error = HostError; + + fn compare( + &self, + a: &ShallowComparableRawVal, + b: &ShallowComparableRawVal, + ) -> Result { + Ok(a.get_payload().cmp(&b.get_payload())) + } +} + +/// A special-purpose map from arbitrary contract-owned values to arbitrary +/// values. +/// +/// Since `TempStorage` stores `RawVal`s, it has to be attributed to the host +/// instance and can't be transferred between host instances. +/// +/// Semantically, this is similar to the `Storage`, but it is never persisted +/// and hence doesn't need to support the footprints and convert keys to the +/// ledger-compatibile types. +#[derive(Clone, Default)] +pub struct TempStorage { + pub map: TempStorageMap, +} + +impl TempStorage { + pub fn get( + &self, + contract_id: [u8; 32], + key: ScVal, + budget: &Budget, + ) -> Result { + match self.map.get(&(contract_id, key), budget)? { + None => Err(ScHostStorageErrorCode::MissingKeyInGet.into()), + Some(val) => Ok(*val), + } + } + + pub fn put( + &mut self, + contract_id: [u8; 32], + key: ScVal, + val: RawVal, + budget: &Budget, + ) -> Result<(), HostError> { + self.map = self.map.insert((contract_id, key), val, budget)?; + Ok(()) + } + + pub fn del( + &mut self, + contract_id: [u8; 32], + key: ScVal, + budget: &Budget, + ) -> Result<(), HostError> { + match self.map.remove(&(contract_id, key), budget)? { + Some((new_self, _)) => { + self.map = new_self; + } + None => (), + }; + Ok(()) + } + + pub fn has( + &mut self, + contract_id: [u8; 32], + key: ScVal, + budget: &Budget, + ) -> Result { + self.map.contains_key(&(contract_id, key), budget) + } +} + #[cfg(test)] mod test_footprint { diff --git a/soroban-test-wasms/wasm-workspace/opt/auth_test_contract.wasm b/soroban-test-wasms/wasm-workspace/opt/auth_test_contract.wasm index 7faf2d7e7..429fe8adc 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/auth_test_contract.wasm and b/soroban-test-wasms/wasm-workspace/opt/auth_test_contract.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_add_i32.wasm b/soroban-test-wasms/wasm-workspace/opt/example_add_i32.wasm index 89c085a7e..e31e5b24d 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_add_i32.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_add_i32.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_complex.wasm b/soroban-test-wasms/wasm-workspace/opt/example_complex.wasm index b6fdb2879..22bb1d7b5 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_complex.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_complex.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_contract_data.wasm b/soroban-test-wasms/wasm-workspace/opt/example_contract_data.wasm index 802966711..8b7e47dd2 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_contract_data.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_contract_data.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_create_contract.wasm b/soroban-test-wasms/wasm-workspace/opt/example_create_contract.wasm index 4ec8318ae..9e29a180a 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_create_contract.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_create_contract.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_fannkuch.wasm b/soroban-test-wasms/wasm-workspace/opt/example_fannkuch.wasm index abb731e22..1b2391c88 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_fannkuch.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_fannkuch.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_fib.wasm b/soroban-test-wasms/wasm-workspace/opt/example_fib.wasm index ba9519d0c..4bdfd5d24 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_fib.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_fib.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_hostile.wasm b/soroban-test-wasms/wasm-workspace/opt/example_hostile.wasm index 9fd597c1f..4342ee390 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_hostile.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_hostile.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_invoke_contract.wasm b/soroban-test-wasms/wasm-workspace/opt/example_invoke_contract.wasm index add788ebf..819459218 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_invoke_contract.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_invoke_contract.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_linear_memory.wasm b/soroban-test-wasms/wasm-workspace/opt/example_linear_memory.wasm index 74a5d7b79..e966f317e 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_linear_memory.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_linear_memory.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_simple_account.wasm b/soroban-test-wasms/wasm-workspace/opt/example_simple_account.wasm index b4df18f3a..e2f4cb1f8 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_simple_account.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_simple_account.wasm differ diff --git a/soroban-test-wasms/wasm-workspace/opt/example_vec.wasm b/soroban-test-wasms/wasm-workspace/opt/example_vec.wasm index 205645efb..07618a51e 100644 Binary files a/soroban-test-wasms/wasm-workspace/opt/example_vec.wasm and b/soroban-test-wasms/wasm-workspace/opt/example_vec.wasm differ