Repository: stellar/rs-soroban-env
Pull request #1105 (2023-10-10 22:22:15)

Cost type cleanup, bug fix, improve calibration
### What

- Update to the latest XDR, consolidating a few memory-related `ContractCostType` -- resolves https://github.com/stellar/rs-soroban-env/issues/1020
- Resolve https://github.com/stellar/rs-soroban-env/issues/1087
- Move some no longer used cost type calibrations (VecEntry, MapEntry) to an `experimental` directory, they are currently not used anywhere but would be useful for experimental purpose. Will do a followup to make them usable.
- Fixed a bug in `memory_grow` function, where we were checking the limit against the wrong input
- Add test helpers to make wasm memory alloc accessible
- Various test fixes, clearifications

### Why

[TODO: Why this change is being made. Include any context required to understand the why.]

### Known limitations

Follow ups:
- Refactor the analytical models
- Make `experimental` directory usable
- Recalibrate numbers on an x86 machine and update the parameters
- Write a more complex test than the currently `complex`, use that for metering benchmark



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index cff2e374..3f961af7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1491,7 +1491,7 @@ dependencies = [
 [[package]]
 name = "stellar-xdr"
 version = "20.0.0-rc1"
-source = "git+https://github.com/stellar/rs-stellar-xdr?rev=4bf4911f0f6ba44e482674a7a9090c22ee97511f#4bf4911f0f6ba44e482674a7a9090c22ee97511f"
+source = "git+https://github.com/stellar/rs-stellar-xdr?rev=9c97e4fa909a0b6455547a4f4a95800696b2a69a#9c97e4fa909a0b6455547a4f4a95800696b2a69a"
 dependencies = [
  "arbitrary",
  "base64 0.13.1",
diff --git a/Cargo.toml b/Cargo.toml
index 5a99d1cc..6a14a3c2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ soroban-native-sdk-macros = { version = "20.0.0-rc2", path = "soroban-native-sdk
 [workspace.dependencies.stellar-xdr]
 version = "20.0.0-rc1"
 git = "https://github.com/stellar/rs-stellar-xdr"
-rev = "4bf4911f0f6ba44e482674a7a9090c22ee97511f"
+rev = "9c97e4fa909a0b6455547a4f4a95800696b2a69a"
 default-features = false
 
 [workspace.dependencies.wasmi]
diff --git a/soroban-env-common/src/meta.rs b/soroban-env-common/src/meta.rs
index e8c28b2e..0159a8b4 100644
--- a/soroban-env-common/src/meta.rs
+++ b/soroban-env-common/src/meta.rs
@@ -58,7 +58,7 @@ soroban_env_macros::generate_env_meta_consts!(
 #[cfg(not(feature = "next"))]
 soroban_env_macros::generate_env_meta_consts!(
     ledger_protocol_version: 20,
-    pre_release_version: 67,
+    pre_release_version: 68,
 );
 
 pub const fn get_ledger_protocol_version(interface_version: u64) -> u32 {
diff --git a/soroban-env-host/benches/common/cost_types/compute_ecdsa_secp256k1_pubkey.rs b/soroban-env-host/benches/common/cost_types/compute_ecdsa_secp256k1_pubkey.rs
deleted file mode 100644
index 2264791a..00000000
--- a/soroban-env-host/benches/common/cost_types/compute_ecdsa_secp256k1_pubkey.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use crate::common::HostCostMeasurement;
-use k256::{PublicKey, SecretKey};
-use rand::rngs::StdRng;
-use soroban_env_host::{cost_runner::ComputeEcdsaSecp256k1PubKeyRun, Host};
-
-// This measures the costs to turn one byte buffer into an EcdsaSecp256k1
-// pubkey, which should be constant time. The input value is ignored.
-pub(crate) struct ComputeEcdsaSecp256k1PubKeyMeasure {
-    key: Vec<u8>,
-}
-
-impl HostCostMeasurement for ComputeEcdsaSecp256k1PubKeyMeasure {
-    type Runner = ComputeEcdsaSecp256k1PubKeyRun;
-
-    fn new_random_case(_host: &Host, _rng: &mut StdRng, _input: u64) -> Vec<u8> {
-        // Very awkward: the 'rand' crate has two copies linked in due to
-        // divergence between the requirements of k256 and ed25519. The StdRng
-        // we're getting here is not the one k256 wants. So we use an OsRng
-        // here, from the package k256 wants (and re-exports).
-        let mut rng = k256::elliptic_curve::rand_core::OsRng;
-
-        let secret = SecretKey::random(&mut rng);
-        let public: PublicKey = secret.public_key();
-        public.to_sec1_bytes().into_vec()
-    }
-}
diff --git a/soroban-env-host/benches/common/cost_types/host_mem_alloc.rs b/soroban-env-host/benches/common/cost_types/host_mem_alloc.rs
index dda0f99a..645c8c26 100644
--- a/soroban-env-host/benches/common/cost_types/host_mem_alloc.rs
+++ b/soroban-env-host/benches/common/cost_types/host_mem_alloc.rs
@@ -1,13 +1,13 @@
 use crate::common::HostCostMeasurement;
 use rand::rngs::StdRng;
-use soroban_env_host::{cost_runner::HostMemAllocRun, Host};
+use soroban_env_host::{cost_runner::MemAllocRun, Host};
 
 // Measures the cost of allocating a chunk of memory in the host. The input
 // value is the number of bytes allocated.
-pub(crate) struct HostMemAllocMeasure;
+pub(crate) struct MemAllocMeasure;
 
-impl HostCostMeasurement for HostMemAllocMeasure {
-    type Runner = HostMemAllocRun;
+impl HostCostMeasurement for MemAllocMeasure {
+    type Runner = MemAllocRun;
 
     fn new_random_case(_host: &Host, _rng: &mut StdRng, input: u64) -> u64 {
         // we just pass along the size and let the runner allocate the memory
diff --git a/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs b/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
index 4e83541a..1e853a84 100644
--- a/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
+++ b/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
@@ -8,9 +8,9 @@ fn randvec(rng: &mut StdRng, len: u64) -> Vec<u8> {
     res
 }
 
-pub struct HostMemCmpMeasure;
-impl HostCostMeasurement for HostMemCmpMeasure {
-    type Runner = HostMemCmpRun;
+pub struct MemCmpMeasure;
+impl HostCostMeasurement for MemCmpMeasure {
+    type Runner = MemCmpRun;
 
     const STEP_SIZE: u64 = 4096;
 
diff --git a/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs b/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
index 320ae1d6..f294bc30 100644
--- a/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
+++ b/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
@@ -1,13 +1,13 @@
 use crate::common::HostCostMeasurement;
 use rand::{rngs::StdRng, RngCore};
-use soroban_env_host::{budget::COST_MODEL_LIN_TERM_SCALE_BITS, cost_runner::HostMemCpyRun, Host};
+use soroban_env_host::{budget::COST_MODEL_LIN_TERM_SCALE_BITS, cost_runner::MemCpyRun, Host};
 
 // Measures the cost of copying a chunk of memory in the host (no allocation).
 // The input value is the number of bytes copied.
-pub(crate) struct HostMemCpyMeasure;
+pub(crate) struct MemCpyMeasure;
 
-impl HostCostMeasurement for HostMemCpyMeasure {
-    type Runner = HostMemCpyRun;
+impl HostCostMeasurement for MemCpyMeasure {
+    type Runner = MemCpyRun;
 
     // Rust and LLVM will conspire to optimize the heck out of a large memcpy.
     // This will cause us to gather completely wrong numbers for the cost of a
diff --git a/soroban-env-host/benches/common/cost_types/mod.rs b/soroban-env-host/benches/common/cost_types/mod.rs
index 128bd36a..75dde9a8 100644
--- a/soroban-env-host/benches/common/cost_types/mod.rs
+++ b/soroban-env-host/benches/common/cost_types/mod.rs
@@ -1,4 +1,3 @@
-mod compute_ecdsa_secp256k1_pubkey;
 mod compute_ecdsa_secp256k1_sig;
 mod compute_ed25519_pubkey;
 mod compute_keccak256_hash;
@@ -7,19 +6,16 @@ mod host_mem_alloc;
 mod host_mem_cmp;
 mod host_mem_cpy;
 mod invoke;
-mod map_ops;
 mod num_ops;
 mod prng;
 mod recover_ecdsa_secp256k1_key;
 mod val_deser;
 mod val_ser;
-mod vec_ops;
 mod verify_ed25519_sig;
 mod visit_object;
 mod vm_ops;
 mod wasm_insn_exec;
 
-pub(crate) use compute_ecdsa_secp256k1_pubkey::*;
 pub(crate) use compute_ecdsa_secp256k1_sig::*;
 pub(crate) use compute_ed25519_pubkey::*;
 pub(crate) use compute_keccak256_hash::*;
@@ -28,13 +24,11 @@ pub(crate) use host_mem_alloc::*;
 pub(crate) use host_mem_cmp::*;
 pub(crate) use host_mem_cpy::*;
 pub(crate) use invoke::*;
-pub(crate) use map_ops::*;
 pub(crate) use num_ops::*;
 pub(crate) use prng::*;
 pub(crate) use recover_ecdsa_secp256k1_key::*;
 pub(crate) use val_deser::*;
 pub(crate) use val_ser::*;
-pub(crate) use vec_ops::*;
 pub(crate) use verify_ed25519_sig::*;
 pub(crate) use visit_object::*;
 pub(crate) use vm_ops::*;
diff --git a/soroban-env-host/benches/common/cost_types/val_deser.rs b/soroban-env-host/benches/common/cost_types/val_deser.rs
index 76bd724f..0c775e8d 100644
--- a/soroban-env-host/benches/common/cost_types/val_deser.rs
+++ b/soroban-env-host/benches/common/cost_types/val_deser.rs
@@ -1,6 +1,6 @@
-use soroban_env_host::{cost_runner::ValDeserRun, xdr::WriteXdr};
+use soroban_env_host::{cost_runner::ValDeserRun, xdr::ScVal, xdr::WriteXdr};
 
-use super::ValSerMeasure;
+use super::{ValSerMeasure, MAX_DEPTH};
 use crate::common::HostCostMeasurement;
 
 pub(crate) struct ValDeserMeasure;
@@ -16,4 +16,27 @@ impl HostCostMeasurement for ValDeserMeasure {
         let scval = ValSerMeasure::new_random_case(host, rng, input);
         scval.0.to_xdr().unwrap()
     }
+
+    // The worst case is a deeply nested structure, each level containing minimal
+    // bytes to move around.
+    fn new_worst_case(
+        _host: &soroban_env_host::Host,
+        _rng: &mut rand::prelude::StdRng,
+        input: u64,
+    ) -> Vec<u8> {
+        let input = 1 + input * Self::STEP_SIZE;
+        let elem_per_level = 1 + input / MAX_DEPTH;
+        let mut v = ScVal::U64(0);
+        let mut rem = input;
+        for _i in 0..MAX_DEPTH {
+            if rem == 0 {
+                break;
+            }
+            let mut inner = vec![v; 1];
+            inner.append(&mut vec![ScVal::U64(0); elem_per_level as usize]);
+            v = ScVal::Vec(Some(inner.try_into().unwrap()));
+            rem -= elem_per_level;
+        }
+        v.to_xdr().unwrap()
+    }
 }
diff --git a/soroban-env-host/benches/common/cost_types/val_ser.rs b/soroban-env-host/benches/common/cost_types/val_ser.rs
index 1b7ba5c5..43c8c3dd 100644
--- a/soroban-env-host/benches/common/cost_types/val_ser.rs
+++ b/soroban-env-host/benches/common/cost_types/val_ser.rs
@@ -1,7 +1,10 @@
 use crate::common::HostCostMeasurement;
 use rand::{rngs::StdRng, RngCore};
+use soroban_env_common::xdr::ScVec;
 use soroban_env_host::{cost_runner::ValSerRun, xdr::ScVal, Host};
 
+pub(crate) const MAX_DEPTH: u64 = 100;
+
 pub(crate) struct ValSerMeasure;
 
 // This measures the costs of converting an ScVal into bytes.
@@ -15,4 +18,24 @@ impl HostCostMeasurement for ValSerMeasure {
         let v = ScVal::Bytes(buf.try_into().unwrap());
         (v, Vec::default())
     }
+
+    // The worst case is a deeply nested structure, each level containing byte arrays of the
+    // given length.
+    fn new_worst_case(_host: &Host, rng: &mut StdRng, input: u64) -> (ScVal, Vec<u8>) {
+        let len = 1 + input * Self::STEP_SIZE;
+        let mut buf = vec![0; len as usize];
+        rng.fill_bytes(buf.as_mut_slice());
+        let scv_bytes = ScVal::Bytes(buf.try_into().unwrap());
+        let mut scv_vec = ScVal::Vec(Some(ScVec(vec![scv_bytes.clone(); 1].try_into().unwrap())));
+        for _i in 1..MAX_DEPTH {
+            let mut v = vec![scv_vec; 1];
+            // We duplicate the bytes 10 times at each level. Because the way `MeteredWrtie` works,
+            // serializing the ScVec will end up calling u32::write_xdr, which has input size 4.
+            // This can create noise with what we want to measure, which is input size len.
+            // Duplicating the bytes array reduces the noise.
+            v.append(&mut vec![scv_bytes.clone(); 10]);
+            scv_vec = ScVal::Vec(Some(v.try_into().unwrap()));
+        }
+        (scv_vec, Vec::default())
+    }
 }
diff --git a/soroban-env-host/benches/common/cost_types/vm_ops.rs b/soroban-env-host/benches/common/cost_types/vm_ops.rs
index 3abe3f88..928ac876 100644
--- a/soroban-env-host/benches/common/cost_types/vm_ops.rs
+++ b/soroban-env-host/benches/common/cost_types/vm_ops.rs
@@ -1,12 +1,10 @@
 #[allow(unused)]
 use super::wasm_insn_exec::{wasm_module_with_4n_insns, wasm_module_with_n_internal_funcs};
 use crate::common::{util, HostCostMeasurement};
-use rand::{rngs::StdRng, Rng, RngCore};
+use rand::{rngs::StdRng, Rng};
 use soroban_env_host::{
-    cost_runner::{
-        VmInstantiationRun, VmInstantiationSample, VmMemReadRun, VmMemRunSample, VmMemWriteRun,
-    },
-    xdr, Host, Vm,
+    cost_runner::{VmInstantiationRun, VmInstantiationSample},
+    xdr, Host,
 };
 
 pub(crate) struct VmInstantiationMeasure;
@@ -45,38 +43,3 @@ impl HostCostMeasurement for VmInstantiationMeasure {
         VmInstantiationSample { id: Some(id), wasm }
     }
 }
-
-// Measures the cost of reading a slice of VM linear memory into a buffer.
-// Input is bytes to read. CPU and memory cost should both be linear.
-// TODO: does this run grow memory if the input exceeds 64kB?
-pub(crate) struct VmMemReadMeasure;
-impl HostCostMeasurement for VmMemReadMeasure {
-    type Runner = VmMemReadRun;
-
-    fn new_random_case(host: &Host, _rng: &mut StdRng, input: u64) -> VmMemRunSample {
-        let input = 1 + input * Self::STEP_SIZE;
-        let buf = vec![0; input as usize];
-        let id: xdr::Hash = [0; 32].into();
-        let code = soroban_test_wasms::ADD_I32;
-        let vm = Vm::new(&host, id, &code).unwrap();
-        VmMemRunSample { vm, buf }
-    }
-}
-
-// Measures the cost of writing into a slice of VM linear memory.
-// Input is bytes to write. CPU and memory cost should both be linear.
-// TODO: does this run grow memory if the input exceeds 64kB?
-pub(crate) struct VmMemWriteMeasure;
-impl HostCostMeasurement for VmMemWriteMeasure {
-    type Runner = VmMemWriteRun;
-
-    fn new_random_case(host: &Host, rng: &mut StdRng, input: u64) -> VmMemRunSample {
-        let input = 1 + input * Self::STEP_SIZE;
-        let mut buf = vec![0; input as usize];
-        rng.fill_bytes(buf.as_mut_slice());
-        let id: xdr::Hash = [0; 32].into();
-        let code = soroban_test_wasms::ADD_I32;
-        let vm = Vm::new(&host, id, &code).unwrap();
-        VmMemRunSample { vm, buf }
-    }
-}
diff --git a/soroban-env-host/benches/common/cost_types/map_ops.rs b/soroban-env-host/benches/common/experimental/map_ops.rs
similarity index 100%
rename from soroban-env-host/benches/common/cost_types/map_ops.rs
rename to soroban-env-host/benches/common/experimental/map_ops.rs
diff --git a/soroban-env-host/benches/common/cost_types/vec_ops.rs b/soroban-env-host/benches/common/experimental/vec_ops.rs
similarity index 100%
rename from soroban-env-host/benches/common/cost_types/vec_ops.rs
rename to soroban-env-host/benches/common/experimental/vec_ops.rs
diff --git a/soroban-env-host/benches/common/experimental/vm_ops.rs b/soroban-env-host/benches/common/experimental/vm_ops.rs
new file mode 100644
index 00000000..943a73d6
--- /dev/null
+++ b/soroban-env-host/benches/common/experimental/vm_ops.rs
@@ -0,0 +1,43 @@
+#[allow(unused)]
+use super::wasm_insn_exec::{wasm_module_with_4n_insns, wasm_module_with_n_internal_funcs};
+use crate::common::{util, HostCostMeasurement};
+use rand::{rngs::StdRng, Rng, RngCore};
+use soroban_env_host::{
+    cost_runner::{VmMemReadRun, VmMemRunSample, VmMemWriteRun},
+    xdr, Host, Vm,
+};
+
+// Measures the cost of reading a slice of VM linear memory into a buffer.
+// Input is bytes to read. CPU and memory cost should both be linear.
+// TODO: does this run grow memory if the input exceeds 64kB?
+pub(crate) struct VmMemReadMeasure;
+impl HostCostMeasurement for VmMemReadMeasure {
+    type Runner = VmMemReadRun;
+
+    fn new_random_case(host: &Host, _rng: &mut StdRng, input: u64) -> VmMemRunSample {
+        let input = 1 + input * Self::STEP_SIZE;
+        let buf = vec![0; input as usize];
+        let id: xdr::Hash = [0; 32].into();
+        let code = soroban_test_wasms::ADD_I32;
+        let vm = Vm::new(&host, id, &code).unwrap();
+        VmMemRunSample { vm, buf }
+    }
+}
+
+// Measures the cost of writing into a slice of VM linear memory.
+// Input is bytes to write. CPU and memory cost should both be linear.
+// TODO: does this run grow memory if the input exceeds 64kB?
+pub(crate) struct VmMemWriteMeasure;
+impl HostCostMeasurement for VmMemWriteMeasure {
+    type Runner = VmMemWriteRun;
+
+    fn new_random_case(host: &Host, rng: &mut StdRng, input: u64) -> VmMemRunSample {
+        let input = 1 + input * Self::STEP_SIZE;
+        let mut buf = vec![0; input as usize];
+        rng.fill_bytes(buf.as_mut_slice());
+        let id: xdr::Hash = [0; 32].into();
+        let code = soroban_test_wasms::ADD_I32;
+        let vm = Vm::new(&host, id, &code).unwrap();
+        VmMemRunSample { vm, buf }
+    }
+}
diff --git a/soroban-env-host/benches/common/mod.rs b/soroban-env-host/benches/common/mod.rs
index bd320dc8..a92a4def 100644
--- a/soroban-env-host/benches/common/mod.rs
+++ b/soroban-env-host/benches/common/mod.rs
@@ -62,7 +62,7 @@ pub(crate) fn for_each_host_cost_measurement<B: Benchmark>(
 ) -> std::io::Result<BTreeMap<ContractCostType, (FPCostModel, FPCostModel)>> {
     let mut params: BTreeMap<ContractCostType, (FPCostModel, FPCostModel)> = BTreeMap::new();
 
-    call_bench::<B, ComputeEcdsaSecp256k1PubKeyMeasure>(&mut params)?;
+    // call_bench::<B, ComputeEcdsaSecp256k1PubKeyMeasure>(&mut params)?;
     call_bench::<B, ComputeEcdsaSecp256k1SigMeasure>(&mut params)?;
     call_bench::<B, ComputeEd25519PubKeyMeasure>(&mut params)?;
     call_bench::<B, ComputeKeccak256HashMeasure>(&mut params)?;
@@ -70,18 +70,18 @@ pub(crate) fn for_each_host_cost_measurement<B: Benchmark>(
     call_bench::<B, RecoverEcdsaSecp256k1KeyMeasure>(&mut params)?;
     call_bench::<B, VerifyEd25519SigMeasure>(&mut params)?;
     call_bench::<B, VmInstantiationMeasure>(&mut params)?;
-    call_bench::<B, VmMemReadMeasure>(&mut params)?;
-    call_bench::<B, VmMemWriteMeasure>(&mut params)?;
+    // call_bench::<B, VmMemReadMeasure>(&mut params)?;
+    // call_bench::<B, VmMemWriteMeasure>(&mut params)?;
     call_bench::<B, VisitObjectMeasure>(&mut params)?;
     call_bench::<B, ValSerMeasure>(&mut params)?;
     call_bench::<B, ValDeserMeasure>(&mut params)?;
-    call_bench::<B, MapEntryMeasure>(&mut params)?;
-    call_bench::<B, VecEntryMeasure>(&mut params)?;
-    call_bench::<B, HostMemCmpMeasure>(&mut params)?;
+    // call_bench::<B, MapEntryMeasure>(&mut params)?;
+    // call_bench::<B, VecEntryMeasure>(&mut params)?;
+    call_bench::<B, MemCmpMeasure>(&mut params)?;
     call_bench::<B, InvokeVmFunctionMeasure>(&mut params)?;
     call_bench::<B, InvokeHostFunctionMeasure>(&mut params)?;
-    call_bench::<B, HostMemAllocMeasure>(&mut params)?;
-    call_bench::<B, HostMemCpyMeasure>(&mut params)?;
+    call_bench::<B, MemAllocMeasure>(&mut params)?;
+    call_bench::<B, MemCpyMeasure>(&mut params)?;
     call_bench::<B, Int256AddSubMeasure>(&mut params)?;
     call_bench::<B, Int256MulMeasure>(&mut params)?;
     call_bench::<B, Int256DivMeasure>(&mut params)?;
diff --git a/soroban-env-host/benches/common/modelfit.rs b/soroban-env-host/benches/common/modelfit.rs
index 1f8b18c9..6e076e9b 100644
--- a/soroban-env-host/benches/common/modelfit.rs
+++ b/soroban-env-host/benches/common/modelfit.rs
@@ -53,12 +53,7 @@ fn fit_linear_regression(x: Vec<f64>, y: Vec<f64>) -> FPCostModel {
         .formula("Y ~ X")
         .fit()
         .unwrap();
-    let params = model.parameters();
     let r2 = model.rsquared();
-    println!(
-        "Linear regression found parameters {:?}, with R2 = {}",
-        params, r2
-    );
     FPCostModel::new(model.parameters(), r2)
 }
 
diff --git a/soroban-env-host/benches/variation_histograms.rs b/soroban-env-host/benches/variation_histograms.rs
index c76fce4f..40232a7b 100644
--- a/soroban-env-host/benches/variation_histograms.rs
+++ b/soroban-env-host/benches/variation_histograms.rs
@@ -2,6 +2,7 @@
 // $ cargo bench --features wasmi,testutils --bench variation_histograms -- --nocapture
 mod common;
 use common::*;
+use soroban_env_host::cost_runner::CostRunner;
 
 struct LinearModelTables;
 impl Benchmark for LinearModelTables {
diff --git a/soroban-env-host/benches/worst_case_linear_models.rs b/soroban-env-host/benches/worst_case_linear_models.rs
index 16cf7e48..1285cde1 100644
--- a/soroban-env-host/benches/worst_case_linear_models.rs
+++ b/soroban-env-host/benches/worst_case_linear_models.rs
@@ -1,7 +1,7 @@
 // Run this with
-// $ cargo bench --features wasmi,testutils --bench worst_case_linear_models -- --nocapture
+// $ cargo bench --features testutils --bench worst_case_linear_models -- --nocapture
 // You can optionally pass in args listing the {`ContractCostType`, `WasmInsnType`} combination to run with, e.g.
-// $ cargo bench --features wasmi,testutils --bench worst_case_linear_models -- VecNew I64Rotr --nocapture
+// $ cargo bench --features testutils --bench worst_case_linear_models -- VecNew I64Rotr --nocapture
 mod common;
 use common::*;
 use soroban_env_host::{
@@ -65,18 +65,6 @@ fn write_budget_params_code(
         base_cpu_per_fuel,
         0
     );
-    println!(
-        "
-        // Host cpu insns per wasm \"memory fuel\". This has to be zero since
-        // the fuel (representing cpu cost) has been covered by `WasmInsnExec`.
-        // The extra cost of mem processing is accounted for by wasmi's
-        // `config.memory_bytes_per_fuel` parameter.
-        // This type is designated to the mem cost. \n
-        ContractCostType::{:?} => {{ cpu.const_term = {}; cpu.lin_term = ScaledU64({}); }}",
-        ContractCostType::WasmMemAlloc,
-        0,
-        0
-    );
 
     for (ty, (cpu, _)) in params
         .iter()
@@ -100,15 +88,6 @@ fn write_budget_params_code(
         0,
         0
     );
-    println!(
-        "
-        // Bytes per wasmi \"memory fuel\". By definition this has to be a const = 1\n
-        // because of the 1-to-1 equivalence of the Wasm mem fuel and a host byte.\n
-        ContractCostType::{:?} => {{ mem.const_term = {}; mem.lin_term = ScaledU64({}); }}",
-        ContractCostType::WasmMemAlloc,
-        1,
-        0
-    );
     for (ty, (_, mem)) in params
         .iter()
         .map(|(ty, (cpu, mem))| (ty, (cpu.params_as_u64(), mem.params_as_u64())))
diff --git a/soroban-env-host/src/budget.rs b/soroban-env-host/src/budget.rs
index 807c55bb..fe0d6276 100644
--- a/soroban-env-host/src/budget.rs
+++ b/soroban-env-host/src/budget.rs
@@ -350,6 +350,8 @@ struct MeterTracker {
     cost_tracker: [(u64, Option<u64>); ContractCostType::variants().len()],
     // Total number of times the meter is called
     count: u32,
+    #[cfg(test)]
+    wasm_memory: u64,
 }
 
 impl MeterTracker {
@@ -359,6 +361,10 @@ impl MeterTracker {
             tracker.0 = 0;
             tracker.1 = tracker.1.map(|_| 0);
         }
+        #[cfg(test)]
+        {
+            self.wasm_memory = 0;
+        }
     }
 }
 
@@ -408,26 +414,27 @@ impl BudgetImpl {
             let i = ct as usize;
             match ct {
                 ContractCostType::WasmInsnExec => (),
-                ContractCostType::WasmMemAlloc => (),
-                ContractCostType::HostMemAlloc => init_input(i), // number of bytes in host memory to allocate
-                ContractCostType::HostMemCpy => init_input(i),   // number of bytes in host to copy
-                ContractCostType::HostMemCmp => init_input(i), // number of bytes in host to compare
+                ContractCostType::MemAlloc => init_input(i), // number of bytes in host memory to allocate
+                ContractCostType::MemCpy => init_input(i),   // number of bytes in host to copy
+                ContractCostType::MemCmp => init_input(i),   // number of bytes in host to compare
                 ContractCostType::DispatchHostFunction => (),
                 ContractCostType::VisitObject => (),
+                // The inputs for `ValSer` and `ValDeser` are subtly different:
+                // `ValSer` works recursively via `WriteXdr`, and each leaf call charges the budget,
+                // and the input is the number of bytes of a leaf entity.
+                // `ValDeser` charges the budget at the top level. Call to `read_xdr` works through
+                // the bytes buffer recursively without worrying about budget charging. So the input
+                // is the length of the total buffer.
+                // This has implication on how their calibration should be set up.
                 ContractCostType::ValSer => init_input(i), // number of bytes in the result buffer
-                ContractCostType::ValDeser => init_input(i), // number of bytes in the buffer
+                ContractCostType::ValDeser => init_input(i), // number of bytes in the input buffer
                 ContractCostType::ComputeSha256Hash => init_input(i), // number of bytes in the buffer
                 ContractCostType::ComputeEd25519PubKey => (),
-                ContractCostType::MapEntry => (),
-                ContractCostType::VecEntry => (),
                 ContractCostType::VerifyEd25519Sig => init_input(i), // length of the signed message
-                ContractCostType::VmMemRead => init_input(i), // number of bytes in the linear memory to read
-                ContractCostType::VmMemWrite => init_input(i), // number of bytes in the linear memory to write
-                ContractCostType::VmInstantiation => init_input(i), // length of the wasm bytes,
+                ContractCostType::VmInstantiation => init_input(i),  // length of the wasm bytes,
                 ContractCostType::VmCachedInstantiation => init_input(i), // length of the wasm bytes,
                 ContractCostType::InvokeVmFunction => (),
                 ContractCostType::ComputeKeccak256Hash => init_input(i), // number of bytes in the buffer
-                ContractCostType::ComputeEcdsaSecp256k1Key => (),
                 ContractCostType::ComputeEcdsaSecp256k1Sig => (),
                 ContractCostType::RecoverEcdsaSecp256k1Key => (),
                 ContractCostType::Int256AddSub => (),
@@ -516,16 +523,7 @@ impl Default for BudgetImpl {
                     cpu.const_term = 6;
                     cpu.lin_term = ScaledU64(0);
                 }
-                // Host cpu insns per wasm "memory fuel". This has to be zero since
-                // the fuel (representing cpu cost) has been covered by `WasmInsnExec`.
-                // The extra cost of mem processing is accounted for by wasmi's
-                // `config.memory_bytes_per_fuel` parameter.
-                // This type is designated to the mem cost.
-                ContractCostType::WasmMemAlloc => {
-                    cpu.const_term = 0;
-                    cpu.lin_term = ScaledU64(0);
-                }
-                ContractCostType::HostMemAlloc => {
+                ContractCostType::MemAlloc => {
                     cpu.const_term = 1141;
                     cpu.lin_term = ScaledU64(1);
                 }
@@ -542,11 +540,11 @@ impl Default for BudgetImpl {
                 // avoid having anything that can be zero cost and approximate
                 // whatever function call, arg-shuffling, spills, reloads or
                 // other flotsam accumulates around a typical memory copy.
-                ContractCostType::HostMemCpy => {
+                ContractCostType::MemCpy => {
                     cpu.const_term = 250;
                     cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
                 }
-                ContractCostType::HostMemCmp => {
+                ContractCostType::MemCmp => {
                     cpu.const_term = 250;
                     cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
                 }
@@ -574,26 +572,10 @@ impl Default for BudgetImpl {
                     cpu.const_term = 25584;
                     cpu.lin_term = ScaledU64(0);
                 }
-                ContractCostType::MapEntry => {
-                    cpu.const_term = 250;
-                    cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
-                }
-                ContractCostType::VecEntry => {
-                    cpu.const_term = 250;
-                    cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
-                }
                 ContractCostType::VerifyEd25519Sig => {
                     cpu.const_term = 376877;
                     cpu.lin_term = ScaledU64(2747);
                 }
-                ContractCostType::VmMemRead => {
-                    cpu.const_term = 1000;
-                    cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
-                }
-                ContractCostType::VmMemWrite => {
-                    cpu.const_term = 1000;
-                    cpu.lin_term = ScaledU64((1 << COST_MODEL_LIN_TERM_SCALE_BITS) / 8);
-                }
                 ContractCostType::VmInstantiation => {
                     cpu.const_term = 967154;
                     cpu.lin_term = ScaledU64(69991);
@@ -610,10 +592,6 @@ impl Default for BudgetImpl {
                     cpu.const_term = 2890;
                     cpu.lin_term = ScaledU64(3561);
                 }
-                ContractCostType::ComputeEcdsaSecp256k1Key => {
-                    cpu.const_term = 38363;
-                    cpu.lin_term = ScaledU64(0);
-                }
                 ContractCostType::ComputeEcdsaSecp256k1Sig => {
                     cpu.const_term = 224;
                     cpu.lin_term = ScaledU64(0);
@@ -657,21 +635,15 @@ impl Default for BudgetImpl {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
                 }
-                // Bytes per wasmi "memory fuel". By definition this has to be a const = 1
-                // because of the 1-to-1 equivalence of the Wasm mem fuel and a host byte.
-                ContractCostType::WasmMemAlloc => {
-                    mem.const_term = 1;
-                    mem.lin_term = ScaledU64(0);
-                }
-                ContractCostType::HostMemAlloc => {
+                ContractCostType::MemAlloc => {
                     mem.const_term = 16;
                     mem.lin_term = ScaledU64(128);
                 }
-                ContractCostType::HostMemCpy => {
+                ContractCostType::MemCpy => {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
                 }
-                ContractCostType::HostMemCmp => {
+                ContractCostType::MemCmp => {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
                 }
@@ -699,26 +671,10 @@ impl Default for BudgetImpl {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
                 }
-                ContractCostType::MapEntry => {
-                    mem.const_term = 0;
-                    mem.lin_term = ScaledU64(0);
-                }
-                ContractCostType::VecEntry => {
-                    mem.const_term = 0;
-                    mem.lin_term = ScaledU64(0);
-                }
                 ContractCostType::VerifyEd25519Sig => {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
                 }
-                ContractCostType::VmMemRead => {
-                    mem.const_term = 0;
-                    mem.lin_term = ScaledU64(0);
-                }
-                ContractCostType::VmMemWrite => {
-                    mem.const_term = 0;
-                    mem.lin_term = ScaledU64(0);
-                }
                 ContractCostType::VmInstantiation => {
                     mem.const_term = 131103;
                     mem.lin_term = ScaledU64(5080);
@@ -735,10 +691,6 @@ impl Default for BudgetImpl {
                     mem.const_term = 40;
                     mem.lin_term = ScaledU64(0);
                 }
-                ContractCostType::ComputeEcdsaSecp256k1Key => {
-                    mem.const_term = 0;
-                    mem.lin_term = ScaledU64(0);
-                }
                 ContractCostType::ComputeEcdsaSecp256k1Sig => {
                     mem.const_term = 0;
                     mem.lin_term = ScaledU64(0);
@@ -1139,6 +1091,18 @@ impl Budget {
         )
     }
 
+    #[cfg(test)]
+    pub(crate) fn track_wasm_mem_alloc(&self, delta: u64) -> Result<(), HostError> {
+        let mut bgt = self.0.try_borrow_mut_or_err()?;
+        bgt.tracker.wasm_memory = bgt.tracker.wasm_memory.saturating_add(delta);
+        Ok(())
+    }
+
+    #[cfg(test)]
+    pub fn get_wasm_mem_alloc(&self) -> Result<u64, HostError> {
+        Ok(self.0.try_borrow_or_err()?.tracker.wasm_memory)
+    }
+
     /// Resets the `FuelConfig` we pass into Wasmi before running calibration.
     /// Wasmi instruction calibration requires running the same Wasmi insn
     /// a fixed number of times, record their actual cpu and mem consumption, then
@@ -1181,7 +1145,8 @@ impl ResourceLimiter for Host {
             .get_mem_bytes_remaining()
             .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
 
-        let allow = if desired as u64 > host_limit {
+        let delta = (desired as u64).saturating_sub(current as u64);
+        let allow = if delta > host_limit {
             false
         } else {
             match maximum {
@@ -1191,9 +1156,15 @@ impl ResourceLimiter for Host {
         };
 
         if allow {
-            let delta = (desired as u64).saturating_sub(current as u64);
+            #[cfg(test)]
+            {
+                self.as_budget()
+                    .track_wasm_mem_alloc(delta)
+                    .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
+            }
+
             self.as_budget()
-                .bulk_charge(ContractCostType::WasmMemAlloc, delta, None)
+                .charge(ContractCostType::MemAlloc, Some(delta))
                 .map(|_| true)
                 .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)
         } else {
diff --git a/soroban-env-host/src/cost_runner/cost_types/compute_ecdsa_secp256k1_pubkey.rs b/soroban-env-host/src/cost_runner/cost_types/compute_ecdsa_secp256k1_pubkey.rs
deleted file mode 100644
index 77347048..00000000
--- a/soroban-env-host/src/cost_runner/cost_types/compute_ecdsa_secp256k1_pubkey.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use std::hint::black_box;
-
-use k256::PublicKey;
-
-use crate::{cost_runner::CostRunner, xdr::ContractCostType};
-
-pub struct ComputeEcdsaSecp256k1PubKeyRun;
-
-impl CostRunner for ComputeEcdsaSecp256k1PubKeyRun {
-    const COST_TYPE: ContractCostType = ContractCostType::ComputeEcdsaSecp256k1Key;
-
-    type SampleType = Vec<u8>;
-
-    type RecycledType = (Option<PublicKey>, Vec<u8>);
-
-    fn run_iter(host: &crate::Host, _iter: u64, sample: Self::SampleType) -> Self::RecycledType {
-        let pk = black_box(
-            host.secp256k1_pub_key_from_bytes(sample.as_slice())
-                .expect("ecdsa secp256k1 publickey"),
-        );
-        (Some(pk), sample)
-    }
-
-    fn run_baseline_iter(
-        host: &crate::Host,
-        _iter: u64,
-        sample: Self::SampleType,
-    ) -> Self::RecycledType {
-        black_box(host.charge_budget(Self::COST_TYPE, None).unwrap());
-        black_box((None, sample))
-    }
-}
diff --git a/soroban-env-host/src/cost_runner/cost_types/host_mem_alloc.rs b/soroban-env-host/src/cost_runner/cost_types/host_mem_alloc.rs
index e4cb17ae..e2ca1566 100644
--- a/soroban-env-host/src/cost_runner/cost_types/host_mem_alloc.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/host_mem_alloc.rs
@@ -2,10 +2,10 @@ use std::hint::black_box;
 
 use crate::{budget::AsBudget, cost_runner::CostRunner, xdr::ContractCostType, MeteredVector};
 
-pub struct HostMemAllocRun;
+pub struct MemAllocRun;
 
-impl CostRunner for HostMemAllocRun {
-    const COST_TYPE: ContractCostType = ContractCostType::HostMemAlloc;
+impl CostRunner for MemAllocRun {
+    const COST_TYPE: ContractCostType = ContractCostType::MemAlloc;
 
     type SampleType = u64;
 
diff --git a/soroban-env-host/src/cost_runner/cost_types/host_mem_cmp.rs b/soroban-env-host/src/cost_runner/cost_types/host_mem_cmp.rs
index 0949130f..a5ac22ab 100644
--- a/soroban-env-host/src/cost_runner/cost_types/host_mem_cmp.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/host_mem_cmp.rs
@@ -4,9 +4,9 @@ use soroban_env_common::Compare;
 
 use crate::{budget::AsBudget, cost_runner::CostRunner, xdr::ContractCostType};
 
-pub struct HostMemCmpRun;
-impl CostRunner for HostMemCmpRun {
-    const COST_TYPE: ContractCostType = ContractCostType::HostMemCmp;
+pub struct MemCmpRun;
+impl CostRunner for MemCmpRun {
+    const COST_TYPE: ContractCostType = ContractCostType::MemCmp;
 
     type SampleType = (Vec<u8>, Vec<u8>);
 
diff --git a/soroban-env-host/src/cost_runner/cost_types/host_mem_cpy.rs b/soroban-env-host/src/cost_runner/cost_types/host_mem_cpy.rs
index 62c94a75..cf1b243c 100644
--- a/soroban-env-host/src/cost_runner/cost_types/host_mem_cpy.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/host_mem_cpy.rs
@@ -2,10 +2,10 @@ use std::hint::black_box;
 
 use crate::{cost_runner::CostRunner, xdr::ContractCostType};
 
-pub struct HostMemCpyRun;
+pub struct MemCpyRun;
 
-impl CostRunner for HostMemCpyRun {
-    const COST_TYPE: ContractCostType = ContractCostType::HostMemCpy;
+impl CostRunner for MemCpyRun {
+    const COST_TYPE: ContractCostType = ContractCostType::MemCpy;
 
     type SampleType = (Vec<u8>, Vec<u8>);
 
diff --git a/soroban-env-host/src/cost_runner/cost_types/mod.rs b/soroban-env-host/src/cost_runner/cost_types/mod.rs
index 620f8fa4..5adb7cb2 100644
--- a/soroban-env-host/src/cost_runner/cost_types/mod.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/mod.rs
@@ -1,4 +1,3 @@
-mod compute_ecdsa_secp256k1_pubkey;
 mod compute_ecdsa_secp256k1_sig;
 mod compute_ed25519_pubkey;
 mod compute_keccak256_hash;
@@ -7,19 +6,16 @@ mod host_mem_alloc;
 mod host_mem_cmp;
 mod host_mem_cpy;
 mod invoke;
-mod map_ops;
 mod num_ops;
 mod prng;
 mod recover_ecdsa_secp256k1_key;
 mod val_deser;
 mod val_ser;
-mod vec_ops;
 mod verify_ed25519_sig;
 mod visit_object;
 mod vm_ops;
 mod wasm_insn_exec;
 
-pub use compute_ecdsa_secp256k1_pubkey::*;
 pub use compute_ecdsa_secp256k1_sig::*;
 pub use compute_ed25519_pubkey::*;
 pub use compute_keccak256_hash::*;
@@ -28,13 +24,11 @@ pub use host_mem_alloc::*;
 pub use host_mem_cmp::*;
 pub use host_mem_cpy::*;
 pub use invoke::*;
-pub use map_ops::*;
 pub use num_ops::*;
 pub use prng::*;
 pub use recover_ecdsa_secp256k1_key::*;
 pub use val_deser::*;
 pub use val_ser::*;
-pub use vec_ops::*;
 pub use verify_ed25519_sig::*;
 pub use visit_object::*;
 pub use vm_ops::*;
diff --git a/soroban-env-host/src/cost_runner/cost_types/val_ser.rs b/soroban-env-host/src/cost_runner/cost_types/val_ser.rs
index d870c21e..20f99ce2 100644
--- a/soroban-env-host/src/cost_runner/cost_types/val_ser.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/val_ser.rs
@@ -10,6 +10,12 @@ pub struct ValSerRun;
 impl CostRunner for ValSerRun {
     const COST_TYPE: ContractCostType = ContractCostType::ValSer;
 
+    // In ValSerMeasure, we are already duplicating the byte array 10 times at each level,
+    // and every level (100 of them) contains a duplication of the same bytes with the same input.
+    // So running this once will run ValSer with the same input 1000 times.
+    // No need to run multiple iterations here.
+    const RUN_ITERATIONS: u64 = 1;
+
     type SampleType = (ScVal, Vec<u8>);
 
     type RecycledType = Self::SampleType;
diff --git a/soroban-env-host/src/cost_runner/cost_types/vm_ops.rs b/soroban-env-host/src/cost_runner/cost_types/vm_ops.rs
index 4868b444..b2889a09 100644
--- a/soroban-env-host/src/cost_runner/cost_types/vm_ops.rs
+++ b/soroban-env-host/src/cost_runner/cost_types/vm_ops.rs
@@ -32,87 +32,3 @@ impl CostRunner for VmInstantiationRun {
         black_box((None, sample.wasm))
     }
 }
-
-#[derive(Clone)]
-pub struct VmMemRunSample {
-    pub vm: Rc<Vm>,
-    pub buf: Vec<u8>,
-}
-
-pub struct VmMemReadRun;
-impl CostRunner for VmMemReadRun {
-    const COST_TYPE: ContractCostType = ContractCostType::VmMemRead;
-
-    type SampleType = VmMemRunSample;
-
-    type RecycledType = Self::SampleType;
-
-    fn run_iter(
-        host: &crate::Host,
-        _iter: u64,
-        mut sample: Self::SampleType,
-    ) -> Self::RecycledType {
-        black_box(
-            sample
-                .vm
-                .with_vmcaller(|caller| {
-                    host.metered_vm_read_bytes_from_linear_memory(
-                        caller,
-                        &sample.vm,
-                        0,
-                        &mut sample.buf,
-                    )
-                })
-                .unwrap(),
-        );
-        sample
-    }
-
-    fn run_baseline_iter(
-        host: &crate::Host,
-        _iter: u64,
-        sample: Self::SampleType,
-    ) -> Self::RecycledType {
-        black_box(host.charge_budget(Self::COST_TYPE, Some(0)).unwrap());
-        black_box(sample)
-    }
-}
-
-pub struct VmMemWriteRun;
-impl CostRunner for VmMemWriteRun {
-    const COST_TYPE: ContractCostType = ContractCostType::VmMemWrite;
-
-    type SampleType = VmMemRunSample;
-
-    type RecycledType = Self::SampleType;
-
-    fn run_iter(
-        host: &crate::Host,
-        _iter: u64,
-        mut sample: Self::SampleType,
-    ) -> Self::RecycledType {
-        black_box(
-            sample
-                .vm
-                .with_vmcaller(|caller| {
-                    host.metered_vm_write_bytes_to_linear_memory(
-                        caller,
-                        &sample.vm,
-                        0,
-                        &mut sample.buf,
-                    )
-                })
-                .unwrap(),
-        );
-        sample
-    }
-
-    fn run_baseline_iter(
-        host: &crate::Host,
-        _iter: u64,
-        sample: Self::SampleType,
-    ) -> Self::RecycledType {
-        black_box(host.charge_budget(Self::COST_TYPE, Some(0)).unwrap());
-        black_box(sample)
-    }
-}
diff --git a/soroban-env-host/src/cost_runner/cost_types/map_ops.rs b/soroban-env-host/src/cost_runner/experimental/map_ops.rs
similarity index 71%
rename from soroban-env-host/src/cost_runner/cost_types/map_ops.rs
rename to soroban-env-host/src/cost_runner/experimental/map_ops.rs
index ace6ff66..d0d5cbad 100644
--- a/soroban-env-host/src/cost_runner/cost_types/map_ops.rs
+++ b/soroban-env-host/src/cost_runner/experimental/map_ops.rs
@@ -15,24 +15,24 @@ pub struct MapEntrySample {
 impl CostRunner for MapEntryRun {
     const COST_TYPE: ContractCostType = ContractCostType::MapEntry;
 
+    const RUN_ITERATIONS: u64 = 1000;
+
     type SampleType = MapEntrySample;
 
-    type RecycledType = (Option<u64>, Self::SampleType);
+    type RecycledType = Self::SampleType;
 
     fn run_iter(host: &crate::Host, iter: u64, sample: Self::SampleType) -> Self::RecycledType {
-        let v = black_box(
+        let _ = black_box(
             sample
                 .map
-                .get(&sample.keys[iter as usize % sample.keys.len()], host)
-                .unwrap()
-                .unwrap()
-                .get_payload(),
+                .get_at_index(iter as usize % sample.keys.len(), host)
+                .unwrap(),
         );
-        (Some(v), sample)
+        sample
     }
 
     fn run_baseline_iter(host: &Host, _iter: u64, sample: Self::SampleType) -> Self::RecycledType {
         black_box(host.charge_budget(Self::COST_TYPE, None).unwrap());
-        black_box((None, sample))
+        black_box(sample)
     }
 }
diff --git a/soroban-env-host/src/cost_runner/cost_types/vec_ops.rs b/soroban-env-host/src/cost_runner/experimental/vec_ops.rs
similarity index 86%
rename from soroban-env-host/src/cost_runner/cost_types/vec_ops.rs
rename to soroban-env-host/src/cost_runner/experimental/vec_ops.rs
index 353624f5..1c072f6c 100644
--- a/soroban-env-host/src/cost_runner/cost_types/vec_ops.rs
+++ b/soroban-env-host/src/cost_runner/experimental/vec_ops.rs
@@ -15,12 +15,14 @@ pub struct VecEntrySample {
 impl CostRunner for VecEntryRun {
     const COST_TYPE: ContractCostType = ContractCostType::VecEntry;
 
+    const RUN_ITERATIONS: u64 = 1000;
+
     type SampleType = VecEntrySample;
 
-    type RecycledType = (Option<u64>, Self::SampleType);
+    type RecycledType = VecEntrySample;
 
     fn run_iter(host: &Host, iter: u64, sample: Self::SampleType) -> Self::RecycledType {
-        let v = black_box(
+        let _ = black_box(
             sample
                 .vec
                 .get(
@@ -30,11 +32,11 @@ impl CostRunner for VecEntryRun {
                 .unwrap()
                 .get_payload(),
         );
-        (Some(v), sample)
+        black_box(sample)
     }
 
     fn run_baseline_iter(host: &Host, _iter: u64, sample: Self::SampleType) -> Self::RecycledType {
         black_box(host.charge_budget(Self::COST_TYPE, None).unwrap());
-        black_box((None, sample))
+        black_box(sample)
     }
 }
diff --git a/soroban-env-host/src/cost_runner/experimental/vm_ops.rs b/soroban-env-host/src/cost_runner/experimental/vm_ops.rs
new file mode 100644
index 00000000..aab1c0c3
--- /dev/null
+++ b/soroban-env-host/src/cost_runner/experimental/vm_ops.rs
@@ -0,0 +1,86 @@
+use crate::{cost_runner::CostRunner, xdr::ContractCostType, xdr::Hash, Vm};
+use std::{hint::black_box, rc::Rc};
+
+#[derive(Clone)]
+pub struct VmMemRunSample {
+    pub vm: Rc<Vm>,
+    pub buf: Vec<u8>,
+}
+
+pub struct VmMemReadRun;
+impl CostRunner for VmMemReadRun {
+    const COST_TYPE: ContractCostType = ContractCostType::VmMemRead;
+
+    type SampleType = VmMemRunSample;
+
+    type RecycledType = Self::SampleType;
+
+    fn run_iter(
+        host: &crate::Host,
+        _iter: u64,
+        mut sample: Self::SampleType,
+    ) -> Self::RecycledType {
+        black_box(
+            sample
+                .vm
+                .with_vmcaller(|caller| {
+                    host.metered_vm_read_bytes_from_linear_memory(
+                        caller,
+                        &sample.vm,
+                        0,
+                        &mut sample.buf,
+                    )
+                })
+                .unwrap(),
+        );
+        sample
+    }
+
+    fn run_baseline_iter(
+        host: &crate::Host,
+        _iter: u64,
+        sample: Self::SampleType,
+    ) -> Self::RecycledType {
+        black_box(host.charge_budget(Self::COST_TYPE, Some(0)).unwrap());
+        black_box(sample)
+    }
+}
+
+pub struct VmMemWriteRun;
+impl CostRunner for VmMemWriteRun {
+    const COST_TYPE: ContractCostType = ContractCostType::VmMemWrite;
+
+    type SampleType = VmMemRunSample;
+
+    type RecycledType = Self::SampleType;
+
+    fn run_iter(
+        host: &crate::Host,
+        _iter: u64,
+        mut sample: Self::SampleType,
+    ) -> Self::RecycledType {
+        black_box(
+            sample
+                .vm
+                .with_vmcaller(|caller| {
+                    host.metered_vm_write_bytes_to_linear_memory(
+                        caller,
+                        &sample.vm,
+                        0,
+                        &mut sample.buf,
+                    )
+                })
+                .unwrap(),
+        );
+        sample
+    }
+
+    fn run_baseline_iter(
+        host: &crate::Host,
+        _iter: u64,
+        sample: Self::SampleType,
+    ) -> Self::RecycledType {
+        black_box(host.charge_budget(Self::COST_TYPE, Some(0)).unwrap());
+        black_box(sample)
+    }
+}
diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs
index b9f66aad..7b8d4026 100644
--- a/soroban-env-host/src/host.rs
+++ b/soroban-env-host/src/host.rs
@@ -527,7 +527,7 @@ impl EnvBase for Host {
     }
 
     fn symbol_new_from_slice(&self, s: &str) -> Result<SymbolObject, HostError> {
-        self.charge_budget(ContractCostType::HostMemCmp, Some(s.len() as u64))?;
+        self.charge_budget(ContractCostType::MemCmp, Some(s.len() as u64))?;
         for ch in s.chars() {
             SymbolSmall::validate_char(ch)?;
         }
@@ -1217,7 +1217,7 @@ impl VmCallerEnv for Host {
             keys_pos,
             len as usize,
             |_n, slice| {
-                self.charge_budget(ContractCostType::VmMemRead, Some(slice.len() as u64))?;
+                self.charge_budget(ContractCostType::MemCpy, Some(slice.len() as u64))?;
                 let scsym = ScSymbol(slice.try_into()?);
                 let sym = Symbol::try_from(self.to_host_val(&ScVal::Symbol(scsym))?)?;
                 key_syms.push(sym);
@@ -2543,7 +2543,7 @@ impl VmCallerEnv for Host {
     ) -> Result<Void, Self::Error> {
         self.visit_obj(seed, |bytes: &ScBytes| {
             let slice: &[u8] = bytes.as_ref();
-            self.charge_budget(ContractCostType::HostMemCpy, Some(prng::SEED_BYTES))?;
+            self.charge_budget(ContractCostType::MemCpy, Some(prng::SEED_BYTES))?;
             if let Ok(seed32) = slice.try_into() {
                 self.with_current_prng(|prng| {
                     *prng = Prng::new_from_seed(seed32);
diff --git a/soroban-env-host/src/host/comparison.rs b/soroban-env-host/src/host/comparison.rs
index c588aefa..01ca2e18 100644
--- a/soroban-env-host/src/host/comparison.rs
+++ b/soroban-env-host/src/host/comparison.rs
@@ -100,10 +100,7 @@ impl Compare<&[u8]> for Budget {
     type Error = HostError;
 
     fn compare(&self, a: &&[u8], b: &&[u8]) -> Result<Ordering, Self::Error> {
-        self.charge(
-            ContractCostType::HostMemCmp,
-            Some(min(a.len(), b.len()) as u64),
-        )?;
+        self.charge(ContractCostType::MemCmp, Some(min(a.len(), b.len()) as u64))?;
         Ok(a.cmp(b))
     }
 }
@@ -112,10 +109,7 @@ impl<const N: usize> Compare<[u8; N]> for Budget {
     type Error = HostError;
 
     fn compare(&self, a: &[u8; N], b: &[u8; N]) -> Result<Ordering, Self::Error> {
-        self.charge(
-            ContractCostType::HostMemCmp,
-            Some(min(a.len(), b.len()) as u64),
-        )?;
+        self.charge(ContractCostType::MemCmp, Some(min(a.len(), b.len()) as u64))?;
         Ok(a.cmp(b))
     }
 }
@@ -146,7 +140,7 @@ impl<T: Ord + DeclaredSizeForMetering> Compare<FixedSizeOrdType<'_, T>> for Budg
             <T as DeclaredSizeForMetering>::DECLARED_SIZE
         );
         self.charge(
-            ContractCostType::HostMemCmp,
+            ContractCostType::MemCmp,
             Some(<T as DeclaredSizeForMetering>::DECLARED_SIZE),
         )?;
         Ok(a.0.cmp(b.0))
diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs
index 39e7de43..19b8d6fa 100644
--- a/soroban-env-host/src/host/conversion.rs
+++ b/soroban-env-host/src/host/conversion.rs
@@ -90,7 +90,7 @@ impl Host {
     {
         match <[u8; N]>::try_from(bytes_arr) {
             Ok(arr) => {
-                self.charge_budget(ContractCostType::HostMemCpy, Some(N as u64))?;
+                self.charge_budget(ContractCostType::MemCpy, Some(N as u64))?;
                 Ok(arr.into())
             }
             Err(_) => Err(err!(
diff --git a/soroban-env-host/src/host/crypto.rs b/soroban-env-host/src/host/crypto.rs
index c88ed4a2..dc82d1c8 100644
--- a/soroban-env-host/src/host/crypto.rs
+++ b/soroban-env-host/src/host/crypto.rs
@@ -74,25 +74,6 @@ impl Host {
 
     // ECDSA secp256k1 functions
 
-    #[cfg(any(test, feature = "testutils"))]
-    // FIXME: this operation is dead code besides the code that claibrates it;
-    // the interface we settled on doesn't involve users converting bytes to
-    // public keys. see https://github.com/stellar/rs-soroban-env/issues/1087
-    pub(crate) fn secp256k1_pub_key_from_bytes(
-        &self,
-        bytes: &[u8],
-    ) -> Result<k256::PublicKey, HostError> {
-        self.charge_budget(ContractCostType::ComputeEcdsaSecp256k1Key, None)?;
-        k256::PublicKey::from_sec1_bytes(bytes).map_err(|_| {
-            self.err(
-                ScErrorType::Crypto,
-                ScErrorCode::InvalidInput,
-                "invalid ECDSA-secp256k1 public key",
-                &[],
-            )
-        })
-    }
-
     pub(crate) fn secp256k1_signature_from_bytes(
         &self,
         bytes: &[u8],
diff --git a/soroban-env-host/src/host/mem_helper.rs b/soroban-env-host/src/host/mem_helper.rs
index 166073e3..d1047a86 100644
--- a/soroban-env-host/src/host/mem_helper.rs
+++ b/soroban-env-host/src/host/mem_helper.rs
@@ -34,7 +34,7 @@ impl Host {
         mem_pos: u32,
         buf: &[u8],
     ) -> Result<(), HostError> {
-        self.charge_budget(ContractCostType::VmMemWrite, Some(buf.len() as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(buf.len() as u64))?;
         let mem = vm.get_memory(self)?;
         self.map_err(
             mem.write(vmcaller.try_mut()?, mem_pos as usize, buf)
@@ -49,7 +49,7 @@ impl Host {
         mem_pos: u32,
         buf: &mut [u8],
     ) -> Result<(), HostError> {
-        self.charge_budget(ContractCostType::VmMemRead, Some(buf.len() as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(buf.len() as u64))?;
         let mem = vm.get_memory(self)?;
         self.map_err(
             mem.read(vmcaller.try_mut()?, mem_pos as usize, buf)
@@ -81,7 +81,7 @@ impl Host {
             .get_mut(mem_range)
             .ok_or_else(|| self.err_oob_linear_memory())?;
 
-        self.charge_budget(ContractCostType::VmMemWrite, Some(byte_len as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(byte_len as u64))?;
         for (src, dst) in buf.iter().zip(mem_slice.chunks_mut(VAL_SZ)) {
             if dst.len() != VAL_SZ {
                 // This should be impossible unless there's an error above, but just in case.
@@ -122,7 +122,7 @@ impl Host {
             .get(mem_range)
             .ok_or_else(|| self.err_oob_linear_memory())?;
 
-        self.charge_budget(ContractCostType::VmMemRead, Some(byte_len as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(byte_len as u64))?;
         let mut tmp: [u8; VAL_SZ] = [0u8; VAL_SZ];
         for (dst, src) in buf.iter_mut().zip(mem_slice.chunks(VAL_SZ)) {
             if let Ok(src) = TryInto::<&[u8; VAL_SZ]>::try_into(src) {
@@ -162,7 +162,7 @@ impl Host {
     ) -> Result<(), HostError> {
         let mem_data = vm.get_memory(self)?.data(vmcaller.try_mut()?);
         self.charge_budget(
-            ContractCostType::VmMemRead,
+            ContractCostType::MemCpy,
             Some((num_slices as u64).saturating_mul(8)),
         )?;
 
@@ -227,7 +227,7 @@ impl Host {
                 &[],
             ));
         }
-        self.charge_budget(ContractCostType::HostMemCpy, Some(dst.len() as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(dst.len() as u64))?;
         dst.copy_from_slice(src);
         Ok(())
     }
@@ -301,7 +301,7 @@ impl Host {
             .ok_or_else(|| self.err_arith_overflow())? as usize;
         if obj_new.len() < obj_end {
             self.charge_budget(
-                ContractCostType::HostMemAlloc,
+                ContractCostType::MemAlloc,
                 Some((obj_end - obj_new.len()) as u64),
             )?;
             obj_new.resize(obj_end, 0);
@@ -352,7 +352,7 @@ impl Host {
         len: U32Val,
     ) -> Result<HOT::Wrapper, HostError> {
         let VmSlice { vm, pos, len } = self.decode_vmslice(lm_pos, len)?;
-        self.charge_budget(ContractCostType::HostMemAlloc, Some(len as u64))?;
+        self.charge_budget(ContractCostType::MemAlloc, Some(len as u64))?;
         let mut vnew: Vec<u8> = vec![0; len as usize];
         self.metered_vm_read_bytes_from_linear_memory(vmcaller, &vm, pos, &mut vnew)?;
         self.add_host_object::<HOT>(vnew.try_into()?)
@@ -396,7 +396,7 @@ impl Host {
         src: &[T],
         dest: &mut [T],
     ) -> Result<(), HostError> {
-        self.charge_budget(ContractCostType::HostMemCpy, Some(src.len() as u64))?;
+        self.charge_budget(ContractCostType::MemCpy, Some(src.len() as u64))?;
         for (s, d) in src.iter().zip(dest.iter_mut()) {
             *d = *s
         }
diff --git a/soroban-env-host/src/host/metered_clone.rs b/soroban-env-host/src/host/metered_clone.rs
index 3be1cc87..72ae4680 100644
--- a/soroban-env-host/src/host/metered_clone.rs
+++ b/soroban-env-host/src/host/metered_clone.rs
@@ -50,7 +50,7 @@ pub(crate) fn charge_shallow_copy<T: DeclaredSizeForMetering>(
         T::DECLARED_SIZE
     );
     budget.as_budget().charge(
-        ContractCostType::HostMemCpy,
+        ContractCostType::MemCpy,
         Some(n_elts.saturating_mul(T::DECLARED_SIZE)),
     )
 }
@@ -70,7 +70,7 @@ pub(crate) fn charge_heap_alloc<T: DeclaredSizeForMetering>(
         T::DECLARED_SIZE
     );
     budget.as_budget().charge(
-        ContractCostType::HostMemAlloc,
+        ContractCostType::MemAlloc,
         Some(n_elts.saturating_mul(T::DECLARED_SIZE)),
     )
 }
diff --git a/soroban-env-host/src/host/metered_map.rs b/soroban-env-host/src/host/metered_map.rs
index dd8050c9..df917760 100644
--- a/soroban-env-host/src/host/metered_map.rs
+++ b/soroban-env-host/src/host/metered_map.rs
@@ -37,20 +37,30 @@ where
     V: DeclaredSizeForMetering,
     Ctx: AsBudget,
 {
+    const ENTRY_SIZE: u64 = <(K, V) as DeclaredSizeForMetering>::DECLARED_SIZE;
+
     fn charge_access<B: AsBudget>(&self, count: usize, b: &B) -> Result<(), HostError> {
-        b.as_budget()
-            .bulk_charge(ContractCostType::MapEntry, count as u64, None)
+        b.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(Self::ENTRY_SIZE * count as u64),
+        )
     }
 
     fn charge_scan<B: AsBudget>(&self, b: &B) -> Result<(), HostError> {
-        b.as_budget()
-            .bulk_charge(ContractCostType::MapEntry, self.map.len() as u64, None)
+        b.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(Self::ENTRY_SIZE * self.map.len() as u64),
+        )
     }
 
+    // Charge binary search includes accessing number of entries expected for
+    // finding an entry. Cost of comparison is charged separately and not covered here.
     fn charge_binsearch<B: AsBudget>(&self, b: &B) -> Result<(), HostError> {
         let mag = 64 - (self.map.len() as u64).leading_zeros();
-        b.as_budget()
-            .bulk_charge(ContractCostType::MapEntry, 1 + mag as u64, None)
+        b.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(Self::ENTRY_SIZE * (1 + mag) as u64),
+        )
     }
 }
 
@@ -297,6 +307,8 @@ where
 
 impl<K, V> Compare<MeteredOrdMap<K, V, Host>> for Host
 where
+    K: DeclaredSizeForMetering,
+    V: DeclaredSizeForMetering,
     Host: Compare<K, Error = HostError> + Compare<V, Error = HostError>,
 {
     type Error = HostError;
@@ -306,10 +318,14 @@ where
         a: &MeteredOrdMap<K, V, Host>,
         b: &MeteredOrdMap<K, V, Host>,
     ) -> Result<Ordering, Self::Error> {
-        self.as_budget().bulk_charge(
-            ContractCostType::MapEntry,
-            a.map.len().min(b.map.len()) as u64,
-            None,
+        // Here covers the cost of accessing number of map entries. The cost of
+        // comparing entries is covered by the `compare` call below.
+        self.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(
+                <(K, V) as DeclaredSizeForMetering>::DECLARED_SIZE
+                    .saturating_mul(a.map.len().min(b.map.len()) as u64),
+            ),
         )?;
         <Self as Compare<Vec<(K, V)>>>::compare(self, &a.map, &b.map)
     }
@@ -317,6 +333,8 @@ where
 
 impl<K, V> Compare<MeteredOrdMap<K, V, Budget>> for Budget
 where
+    K: DeclaredSizeForMetering,
+    V: DeclaredSizeForMetering,
     Budget: Compare<K, Error = HostError> + Compare<V, Error = HostError>,
 {
     type Error = HostError;
@@ -326,10 +344,14 @@ where
         a: &MeteredOrdMap<K, V, Budget>,
         b: &MeteredOrdMap<K, V, Budget>,
     ) -> Result<Ordering, Self::Error> {
-        self.bulk_charge(
-            ContractCostType::MapEntry,
-            a.map.len().min(b.map.len()) as u64,
-            None,
+        // Here covers the cost of accessing number of map entries. The cost of
+        // comparing entries is covered by the `compare` call below.
+        self.charge(
+            ContractCostType::MemCpy,
+            Some(
+                <(K, V) as DeclaredSizeForMetering>::DECLARED_SIZE
+                    .saturating_mul(a.map.len().min(b.map.len()) as u64),
+            ),
         )?;
         <Self as Compare<Vec<(K, V)>>>::compare(self, &a.map, &b.map)
     }
diff --git a/soroban-env-host/src/host/metered_vector.rs b/soroban-env-host/src/host/metered_vector.rs
index 87332677..8a15c407 100644
--- a/soroban-env-host/src/host/metered_vector.rs
+++ b/soroban-env-host/src/host/metered_vector.rs
@@ -31,16 +31,27 @@ where
     A: DeclaredSizeForMetering,
 {
     fn charge_access(&self, count: usize, budget: &Budget) -> Result<(), HostError> {
-        budget.bulk_charge(ContractCostType::VecEntry, count as u64, None)
+        budget.charge(
+            ContractCostType::MemCpy,
+            Some(A::DECLARED_SIZE.saturating_mul(count as u64)),
+        )
     }
 
     fn charge_scan(&self, budget: &Budget) -> Result<(), HostError> {
-        budget.bulk_charge(ContractCostType::VecEntry, self.vec.len() as u64, None)
+        budget.charge(
+            ContractCostType::MemCpy,
+            Some(A::DECLARED_SIZE.saturating_mul(self.vec.len() as u64)),
+        )
     }
 
+    // Charge binary search includes accessing number of entries expected for
+    // finding an entry. Cost of comparison is charged separately and not covered here.
     fn charge_binsearch(&self, budget: &Budget) -> Result<(), HostError> {
         let mag = 64 - (self.vec.len() as u64).leading_zeros();
-        budget.bulk_charge(ContractCostType::VecEntry, 1 + mag as u64, None)
+        budget.charge(
+            ContractCostType::MemCpy,
+            Some(A::DECLARED_SIZE.saturating_mul((1 + mag) as u64)),
+        )
     }
 }
 
@@ -342,10 +353,11 @@ where
         a: &MeteredVector<Elt>,
         b: &MeteredVector<Elt>,
     ) -> Result<Ordering, Self::Error> {
-        self.as_budget().bulk_charge(
-            ContractCostType::VecEntry,
-            a.vec.len().min(b.vec.len()) as u64,
-            None,
+        // Here covers the cost of accessing number of map entries. The cost of
+        // comparing entries is covered by the `compare` call below.
+        self.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(Elt::DECLARED_SIZE.saturating_mul(a.vec.len().min(b.vec.len()) as u64)),
         )?;
         <Self as Compare<Vec<Elt>>>::compare(self, &a.vec, &b.vec)
     }
@@ -362,10 +374,11 @@ where
         a: &MeteredVector<Elt>,
         b: &MeteredVector<Elt>,
     ) -> Result<Ordering, Self::Error> {
-        self.as_budget().bulk_charge(
-            ContractCostType::VecEntry,
-            a.vec.len().min(b.vec.len()) as u64,
-            None,
+        // Here covers the cost of accessing number of map entries. The cost of
+        // comparing entries is covered by the `compare` call below.
+        self.as_budget().charge(
+            ContractCostType::MemCpy,
+            Some(Elt::DECLARED_SIZE.saturating_mul(a.vec.len().min(b.vec.len()) as u64)),
         )?;
         <Self as Compare<Vec<Elt>>>::compare(self, &a.vec, &b.vec)
     }
diff --git a/soroban-env-host/src/host/prng.rs b/soroban-env-host/src/host/prng.rs
index 876b827e..820f8615 100644
--- a/soroban-env-host/src/host/prng.rs
+++ b/soroban-env-host/src/host/prng.rs
@@ -122,7 +122,7 @@ impl Prng {
         let mut v2 = v.metered_clone(budget)?;
         // We charge for both the PRNG draws and the swaps here (as "memcpys").
         self.charge_prng_bytes(budget, 16u64.saturating_mul(v.len() as u64))?;
-        budget.charge(ContractCostType::HostMemCpy, Some(v.len() as u64))?;
+        budget.charge(ContractCostType::MemCpy, Some(v.len() as u64))?;
         v2.as_mut_slice().shuffle(&mut self.0);
         Ok(v2)
     }
@@ -137,7 +137,7 @@ impl Prng {
     pub(crate) fn sub_prng(&mut self, budget: &Budget) -> Result<Prng, HostError> {
         let mut new_seed: Seed = [0; SEED_BYTES as usize];
         chacha20_fill_bytes(&mut self.0, &mut new_seed, budget)?;
-        budget.charge(ContractCostType::HostMemCpy, Some(SEED_BYTES))?;
+        budget.charge(ContractCostType::MemCpy, Some(SEED_BYTES))?;
         Ok(Self(ChaCha20Rng::from_seed(new_seed)))
     }
 }
diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs
index 09542712..3417249b 100644
--- a/soroban-env-host/src/native_contract/token/metadata.rs
+++ b/soroban-env-host/src/native_contract/token/metadata.rs
@@ -141,10 +141,7 @@ fn render_sep0011_asset<const N: usize>(
     // We also have to charge for strkey_len again since PublicKey::to_string does
     // a std::string::String allocation of its own.
     let charge = capacity + strkey_len;
-    e.charge_budget(
-        crate::xdr::ContractCostType::HostMemAlloc,
-        Some(charge as u64),
-    )?;
+    e.charge_budget(crate::xdr::ContractCostType::MemAlloc, Some(charge as u64))?;
 
     let mut s: std::string::String = std::string::String::with_capacity(capacity);
     render_sep0011_asset_code(&symbuf, &mut s)?;
diff --git a/soroban-env-host/src/vm.rs b/soroban-env-host/src/vm.rs
index 817d3d54..cfd52828 100644
--- a/soroban-env-host/src/vm.rs
+++ b/soroban-env-host/src/vm.rs
@@ -422,6 +422,7 @@ impl Vm {
     /// to this VM's `Store` and `Instance`, and calls the provided function
     /// back with it. Mainly used for testing.
     #[cfg(any(test, feature = "testutils"))]
+    #[allow(unused)]
     pub(crate) fn with_vmcaller<F, T>(&self, f: F) -> Result<T, HostError>
     where
         F: FnOnce(&mut VmCaller<Host>) -> Result<T, HostError>,


Test patch:
diff --git a/soroban-env-host/src/test/budget_metering.rs b/soroban-env-host/src/test/budget_metering.rs
index 1070450b..fe800e96 100644
--- a/soroban-env-host/src/test/budget_metering.rs
+++ b/soroban-env-host/src/test/budget_metering.rs
@@ -13,7 +13,7 @@ use soroban_test_wasms::VEC;
 fn xdr_object_conversion() -> Result<(), HostError> {
     let host = Host::test_host()
         .test_budget(100_000, 100_000)
-        .enable_model(ContractCostType::HostMemCpy, 1, 0, 1, 0);
+        .enable_model(ContractCostType::MemCpy, 1, 0, 1, 0);
     let scmap: ScMap = host.map_err(
         vec![
             ScMapEntry {
@@ -29,13 +29,15 @@ fn xdr_object_conversion() -> Result<(), HostError> {
     )?;
     host.to_host_val(&ScVal::Map(Some(scmap)))?;
     host.with_budget(|budget| {
-        // 2 iterations, 1 for the vec cpy, 1 for the bulk bytes cpy
-        assert_eq!(budget.get_tracker(ContractCostType::HostMemCpy)?.0, 2);
-        // 72 bytes copied for the ScVal->Val conversion: 24 (Vec bytes) + 2 (map entries) x (8 (padding bytes) + 8 (key bytes) + 8 (val bytes))
-        assert_eq!(
-            budget.get_tracker(ContractCostType::HostMemCpy)?.1,
-            Some(72)
-        );
+        // 3 iterations:
+        // - 1 for the vec cpy
+        // - 1 for the bulk bytes cpy
+        // - 1 for Vec -> MeteredOrdMap element scan
+        assert_eq!(budget.get_tracker(ContractCostType::MemCpy)?.0, 3);
+        // 120 bytes in total:
+        // - 72 bytes copied for the ScVal->Val conversion: 24 (Vec bytes) + 2 (map entries) x (8 (padding bytes) + 8 (key bytes) + 8 (val bytes))
+        // - 48 bytes for element scan: 2 elements * 24 bytes per element (8 padding bytes + 8 key bytes + 8 val bytes)
+        assert_eq!(budget.get_tracker(ContractCostType::MemCpy)?.1, Some(120));
         Ok(())
     })?;
     Ok(())
@@ -87,19 +89,19 @@ fn test_vm_fuel_metering() -> Result<(), HostError> {
     let host = host
         .test_budget(100_000, 1_048_576)
         .enable_model(ContractCostType::WasmInsnExec, 6, 0, 0, 0)
-        .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 0, 0, 0, 1);
     host.call(id_obj, sym, args)?;
-    let (cpu_count, mem_count, cpu_consumed, mem_consumed) = host.with_budget(|budget| {
+    let (cpu_count, cpu_consumed, mem_consumed, wasm_mem_alloc) = host.with_budget(|budget| {
         Ok((
             budget.get_tracker(ContractCostType::WasmInsnExec)?.0,
-            budget.get_tracker(ContractCostType::WasmMemAlloc)?.0,
             budget.get_cpu_insns_consumed()?,
             budget.get_mem_bytes_consumed()?,
+            budget.get_wasm_mem_alloc()?,
         ))
     })?;
     assert_eq!(
-        (cpu_count, mem_count, cpu_consumed, mem_consumed),
-        (4005, 65536, 24030, 65536)
+        (cpu_count, cpu_consumed, wasm_mem_alloc),
+        (4005, 24030, 65536)
     );
 
     // giving it the exact required amount will success
@@ -107,11 +109,11 @@ fn test_vm_fuel_metering() -> Result<(), HostError> {
     let host = host
         .test_budget(cpu_required, mem_required)
         .enable_model(ContractCostType::WasmInsnExec, 6, 0, 0, 0)
-        .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 0, 0, 0, 1);
     host.call(id_obj, sym, args)?;
     host.with_budget(|budget| {
         assert_eq!(budget.get_cpu_insns_consumed()?, cpu_required);
-        assert_eq!(budget.get_mem_bytes_consumed()?, mem_required);
+        assert_eq!(budget.get_wasm_mem_alloc()?, wasm_mem_alloc);
         Ok(())
     })?;
 
@@ -120,26 +122,26 @@ fn test_vm_fuel_metering() -> Result<(), HostError> {
     let host = host
         .test_budget(cpu_required, mem_required)
         .enable_model(ContractCostType::WasmInsnExec, 6, 0, 0, 0)
-        .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 0, 0, 0, 1);
     let res = host.try_call(id_obj, sym, args);
     assert!(HostError::result_matches_err(res, budget_err));
     host.with_budget(|budget| {
         assert_eq!(budget.get_cpu_insns_consumed()?, 0);
-        assert_eq!(budget.get_mem_bytes_consumed()?, mem_consumed);
+        assert_eq!(budget.get_wasm_mem_alloc()?, wasm_mem_alloc);
         Ok(())
     })?;
 
-    // give it one less mem results in failure with no cpu consumption or mem consumption
-    let (cpu_required, mem_required) = (cpu_consumed, mem_consumed - 1);
+    // give it less than 1 page of memory in failure with no cpu consumption or mem consumption
+    let (cpu_required, mem_required) = (cpu_consumed, 65535);
     let host = host
         .test_budget(cpu_required, mem_required)
         .enable_model(ContractCostType::WasmInsnExec, 6, 0, 0, 0)
-        .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 0, 0, 0, 1);
     let res = host.try_call(id_obj, sym, args);
     assert!(HostError::result_matches_err(res, budget_err));
     host.with_budget(|budget| {
         assert_eq!(budget.get_cpu_insns_consumed()?, 0);
-        assert_eq!(budget.get_mem_bytes_consumed()?, 0);
+        assert_eq!(budget.get_wasm_mem_alloc()?, 0);
         Ok(())
     })?;
 
@@ -223,9 +225,7 @@ fn map_insert_key_vec_obj() -> Result<(), HostError> {
     m = host.map_put(m, k0.into(), v0)?;
 
     // now we enable various cost models
-    host = host
-        .enable_model(ContractCostType::VisitObject, 10, 0, 1, 0)
-        .enable_model(ContractCostType::MapEntry, 10, 0, 1, 0);
+    host = host.enable_model(ContractCostType::VisitObject, 10, 0, 1, 0);
     host.map_put(m, k1.into(), v1)?;
 
     host.with_budget(|budget| {
@@ -240,7 +240,6 @@ fn map_insert_key_vec_obj() -> Result<(), HostError> {
         // = 12
         assert_eq!(budget.get_tracker(ContractCostType::VisitObject)?.0, 12);
         // upper bound of number of map-accesses, counting both binary-search, point-access and validate-scan.
-        assert_eq!(budget.get_tracker(ContractCostType::MapEntry)?.0, 8);
         Ok(())
     })?;
 
@@ -251,8 +250,8 @@ fn map_insert_key_vec_obj() -> Result<(), HostError> {
 fn test_recursive_type_clone() -> Result<(), HostError> {
     let host = Host::test_host()
         .test_budget(100000, 100000)
-        .enable_model(ContractCostType::HostMemAlloc, 10, 0, 1, 0)
-        .enable_model(ContractCostType::HostMemCpy, 10, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 10, 0, 1, 0)
+        .enable_model(ContractCostType::MemCpy, 10, 0, 1, 0);
     let scmap: ScMap = host.map_err(
         vec![
             ScMapEntry {
@@ -281,7 +280,7 @@ fn test_recursive_type_clone() -> Result<(), HostError> {
     //*********************************************************************************************************************************************/
     expect!["864"].assert_eq(
         host.as_budget()
-            .get_tracker(ContractCostType::HostMemAlloc)?
+            .get_tracker(ContractCostType::MemAlloc)?
             .1
             .unwrap()
             .to_string()
@@ -291,7 +290,7 @@ fn test_recursive_type_clone() -> Result<(), HostError> {
     // memory layout of the top level type (Vec).
     expect!["888"].assert_eq(
         host.as_budget()
-            .get_tracker(ContractCostType::HostMemCpy)?
+            .get_tracker(ContractCostType::MemCpy)?
             .1
             .unwrap()
             .to_string()
@@ -322,7 +321,6 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
 
     let tracker: Vec<(u64, Option<u64>)> = vec![
         (246, None),
-        (184, None),
         (1, Some(152)),
         (1, Some(65)),
         (1, Some(74)),
@@ -332,11 +330,7 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
         (1, Some(103)),
         (1, Some(193)),
         (226, None),
-        (250, None),
-        (186, None),
         (1, Some(227)),
-        (1, Some(69)),
-        (1, Some(160)),
         (1, Some(147)),
         (1, Some(147)),
         (47, None),
@@ -348,7 +342,6 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
         (1, None),
         (1, None),
         (1, None),
-        (1, None),
         (1, Some(1)),
     ];
 
@@ -358,31 +351,25 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
     let actual = format!("{:?}", host.as_budget());
     expect![[r#"
         =====================================================================================================================================================================
-        Cpu limit: 100000000; used: 10218283
-        Mem limit: 41943040; used: 276044
+        Cpu limit: 100000000; used: 10068892
+        Mem limit: 41943040; used: 275860
         =====================================================================================================================================================================
         CostType                 iterations     input          cpu_insns      mem_bytes      const_term_cpu      lin_term_cpu        const_term_mem      lin_term_mem        
         WasmInsnExec             246            None           1476           0              6                   0                   0                   0                   
-        WasmMemAlloc             184            None           0              184            0                   0                   1                   0                   
-        HostMemAlloc             1              Some(152)      1142           168            1141                1                   16                  128                 
-        HostMemCpy               1              Some(65)       258            0              250                 16                  0                   0                   
-        HostMemCmp               1              Some(74)       259            0              250                 16                  0                   0                   
+        MemAlloc                 1              Some(152)      1142           168            1141                1                   16                  128                 
+        MemCpy                   1              Some(65)       258            0              250                 16                  0                   0                   
+        MemCmp                   1              Some(74)       259            0              250                 16                  0                   0                   
         DispatchHostFunction     176            None           46288          0              263                 0                   0                   0                   
         VisitObject              97             None           10476          0              108                 0                   0                   0                   
         ValSer                   1              Some(49)       1006           165            1000                16                  18                  384                 
         ValDeser                 1              Some(103)      1012           119            1000                16                  16                  128                 
         ComputeSha256Hash        1              Some(193)      9179           40             2924                4149                40                  0                   
         ComputeEd25519PubKey     226            None           5781984        0              25584               0                   0                   0                   
-        MapEntry                 250            None           62500          0              250                 16                  0                   0                   
-        VecEntry                 186            None           46500          0              250                 16                  0                   0                   
         VerifyEd25519Sig         1              Some(227)      381748         0              376877              2747                0                   0                   
-        VmMemRead                1              Some(69)       1008           0              1000                16                  0                   0                   
-        VmMemWrite               1              Some(160)      1020           0              1000                16                  0                   0                   
         VmInstantiation          1              Some(147)      1047534        136937         967154              69991               131103              5080                
         VmCachedInstantiation    1              Some(147)      1047534        136937         967154              69991               131103              5080                
         InvokeVmFunction         47             None           52875          658            1125                0                   14                  0                   
         ComputeKeccak256Hash     1              Some(1)        2917           40             2890                3561                40                  0                   
-        ComputeEcdsaSecp256k1Key 1              None           38363          0              38363               0                   0                   0                   
         ComputeEcdsaSecp256k1Sig 1              None           224            0              224                 0                   0                   0                   
         RecoverEcdsaSecp256k1Key 1              None           1666155        201            1666155             0                   201                 0                   
         Int256AddSub             1              None           1716           119            1716                0                   119                 0                   
@@ -392,7 +379,7 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
         Int256Shift              1              None           412            119            412                 0                   119                 0                   
         ChaCha20DrawBytes        1              Some(1)        4926           0              4907                2461                0                   0                   
         =====================================================================================================================================================================
-        Total # times meter was called: 29
+        Total # times meter was called: 23
 
     "#]]
     .assert_eq(&actual);
diff --git a/soroban-env-host/src/test/event.rs b/soroban-env-host/src/test/event.rs
index 70e5ecbf..2bfab55d 100644
--- a/soroban-env-host/src/test/event.rs
+++ b/soroban-env-host/src/test/event.rs
@@ -116,8 +116,8 @@ fn test_internal_contract_events_metering_not_free() -> Result<(), HostError> {
 
     let host = host
         .test_budget(100000, 100000)
-        .enable_model(ContractCostType::HostMemAlloc, 10, 0, 1, 0)
-        .enable_model(ContractCostType::HostMemCpy, 10, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 10, 0, 1, 0)
+        .enable_model(ContractCostType::MemCpy, 10, 0, 1, 0);
 
     let _ = host.with_events_mut(|events| {
         Ok(events.record(InternalEvent::Contract(ce), host.as_budget()))
@@ -149,8 +149,8 @@ fn test_internal_diagnostic_event_metering_free() -> Result<(), HostError> {
 
     let host = host
         .test_budget(100000, 100000)
-        .enable_model(ContractCostType::HostMemAlloc, 10, 0, 1, 0)
-        .enable_model(ContractCostType::HostMemCpy, 10, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 10, 0, 1, 0)
+        .enable_model(ContractCostType::MemCpy, 10, 0, 1, 0);
 
     let _ = host.with_events_mut(|events| {
         Ok(events.record(InternalEvent::Diagnostic(de), host.as_budget()))
diff --git a/soroban-env-host/src/test/hostile.rs b/soroban-env-host/src/test/hostile.rs
index 44f1b9fe..f45a07f6 100644
--- a/soroban-env-host/src/test/hostile.rs
+++ b/soroban-env-host/src/test/hostile.rs
@@ -220,18 +220,19 @@ fn wasm_module_with_mem_grow(n_pages: usize) -> Vec<u8> {
 
 #[test]
 fn excessive_memory_growth() -> Result<(), HostError> {
-    // not sure why calling `memory_grow(32)`, wasmi requests 33 pages of memory
+    // `memory_grow(32)`, wasmi will desire 33 pages of memory, that includes the
+    // initial page.
     let wasm = wasm_module_with_mem_grow(32);
     let host = Host::test_host_with_recording_footprint();
     let contract_id_obj = host.register_test_contract_wasm(wasm.as_slice());
     let host = host
         .test_budget(0, 0)
-        .enable_model(ContractCostType::WasmMemAlloc, 0, 0, 1, 0);
+        .enable_model(ContractCostType::MemAlloc, 0, 0, 0, 1);
     host.set_diagnostic_level(crate::DiagnosticLevel::Debug)?;
 
-    // This one should just run out of memory
+    // This one should just run out of memory.
     {
-        let mem_budget = 33 * 0x10_000;
+        let mem_budget = 32 * 0x10_000;
         host.as_budget().reset_limits(40_000, mem_budget)?;
         let res = host.call(
             contract_id_obj,
@@ -242,11 +243,13 @@ fn excessive_memory_growth() -> Result<(), HostError> {
             res,
             (ScErrorType::Budget, ScErrorCode::ExceededLimit)
         ));
+        // only the intial page has been allocated
+        assert_eq!(host.as_budget().get_wasm_mem_alloc()?, 0x10_000);
     }
 
-    // allowing one extra page should be okay
+    // giving it 33 pages plus some small overhead should be okay
     {
-        let mem_budget = 34 * 0x10_000;
+        let mem_budget = 33 * 0x10_000 + 2000;
         // Note on requiring non-zero cpu limit: even though no cpu instruction
         // is consumed, wasmi does require checking internally there is enough fuel
         // to finish the task. memory_grow is a special instruction that requires extra
@@ -259,6 +262,8 @@ fn excessive_memory_growth() -> Result<(), HostError> {
             host.add_host_object(HostVec::new())?,
         );
         assert!(res.is_ok());
+        // initial 1 page + 32 extra pages has been allocated
+        assert_eq!(host.as_budget().get_wasm_mem_alloc()?, 33 * 0x10_000);
     }
 
     Ok(())
diff --git a/soroban-env-host/tests/fees.rs b/soroban-env-host/tests/fees.rs
index 210cc54c..92f7730e 100644
--- a/soroban-env-host/tests/fees.rs
+++ b/soroban-env-host/tests/fees.rs
@@ -7,7 +7,7 @@ use soroban_env_host::fees::{
 use soroban_env_host::xdr::TtlEntry;
 
 #[test]
-fn expiration_entry_size() {
+fn ttl_entry_size() {
     let expiration_entry = LedgerEntry {
         last_modified_ledger_seq: 0,
         data: LedgerEntryData::Ttl(TtlEntry {
diff --git a/soroban-test-wasms/wasm-workspace/Cargo.lock b/soroban-test-wasms/wasm-workspace/Cargo.lock
index 11a049f7..6845e907 100644
--- a/soroban-test-wasms/wasm-workspace/Cargo.lock
+++ b/soroban-test-wasms/wasm-workspace/Cargo.lock
@@ -1237,7 +1237,7 @@ dependencies = [
 [[package]]
 name = "stellar-xdr"
 version = "20.0.0-rc1"
-source = "git+https://github.com/stellar/rs-stellar-xdr?rev=4bf4911f0f6ba44e482674a7a9090c22ee97511f#4bf4911f0f6ba44e482674a7a9090c22ee97511f"
+source = "git+https://github.com/stellar/rs-stellar-xdr?rev=9c97e4fa909a0b6455547a4f4a95800696b2a69a#9c97e4fa909a0b6455547a4f4a95800696b2a69a"
 dependencies = [
  "arbitrary",
  "base64 0.13.1",