diff --git a/soroban-sdk/src/map.rs b/soroban-sdk/src/map.rs index be6f85071..5ac2efa58 100644 --- a/soroban-sdk/src/map.rs +++ b/soroban-sdk/src/map.rs @@ -8,7 +8,7 @@ use crate::{ }; use super::{ - env::internal::{Env as _, EnvBase as _, MapObject}, + env::internal::{Env as _, EnvBase as _, MapObject, U32Val}, ConversionError, Env, IntoVal, TryFromVal, TryIntoVal, Val, Vec, }; @@ -508,15 +508,15 @@ where #[derive(Clone)] pub struct MapTryIter { map: Map, - pos: u32, - len: u32, + begin: u32, + end: u32, } impl MapTryIter { fn new(map: Map) -> Self { Self { - pos: 0, - len: map.len(), + begin: 0, + end: map.len(), map, } } @@ -531,16 +531,15 @@ where fn next(&mut self) -> Option { let env = self.map.env(); - if self.pos == self.len { + if self.begin >= self.end { return None; } - let key = env - .map_key_by_pos(self.map.to_object(), self.pos.into()) - .unwrap_infallible(); - let value = env - .map_val_by_pos(self.map.to_object(), self.pos.into()) - .unwrap_infallible(); - self.pos += 1; + let map_obj = self.map.to_object(); + let index_val: U32Val = self.begin.into(); + let key = env.map_key_by_pos(map_obj, index_val).unwrap_infallible(); + let value = env.map_val_by_pos(map_obj, index_val).unwrap_infallible(); + self.begin += 1; + Some(Ok(( match K::try_from_val(env, &key) { Ok(k) => k, @@ -554,7 +553,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let len = (self.len - self.pos) as usize; + let len = (self.end - self.begin) as usize; (len, Some(len)) } @@ -568,16 +567,15 @@ where { fn next_back(&mut self) -> Option { let env = self.map.env(); - if self.pos == self.len { + if self.begin >= self.end { return None; } - self.len -= 1; - let key = env - .map_key_by_pos(self.map.to_object(), self.len.into()) - .unwrap_infallible(); - let value = env - .map_val_by_pos(self.map.to_object(), self.len.into()) - .unwrap_infallible(); + self.end -= 1; + let map_obj = self.map.to_object(); + let index_val: U32Val = self.end.into(); + let key = env.map_key_by_pos(map_obj, index_val).unwrap_infallible(); + let value = env.map_val_by_pos(map_obj, index_val).unwrap_infallible(); + Some(Ok(( match K::try_from_val(env, &key) { Ok(k) => k, @@ -606,7 +604,7 @@ where V: IntoVal + TryFromVal, { fn len(&self) -> usize { - (self.len - self.pos) as usize + (self.end - self.begin) as usize } } diff --git a/soroban-sdk/src/storage.rs b/soroban-sdk/src/storage.rs index 6b6fa78e9..3d8f7da60 100644 --- a/soroban-sdk/src/storage.rs +++ b/soroban-sdk/src/storage.rs @@ -363,14 +363,10 @@ impl Instance { self.storage.remove(key, StorageType::Instance) } - pub fn bump(&self, _min_ledgers_to_live: u32) { - // This is required because register_contract - // doesn't create an instance. This guard can be - // removed once that is fixed. - #[cfg(not(any(test, feature = "testutils")))] + pub fn bump(&self, min_ledgers_to_live: u32) { internal::Env::bump_current_contract_instance_and_code( &self.storage.env, - _min_ledgers_to_live.into(), + min_ledgers_to_live.into(), ) .unwrap_infallible(); } diff --git a/soroban-sdk/src/tests/contract_store.rs b/soroban-sdk/src/tests/contract_store.rs index e49559d68..c884ab756 100644 --- a/soroban-sdk/src/tests/contract_store.rs +++ b/soroban-sdk/src/tests/contract_store.rs @@ -1,13 +1,50 @@ use crate::{self as soroban_sdk}; -use soroban_sdk::{contract, contractimpl, Env}; +use soroban_sdk::{contract, contractimpl, contracttype, Env}; + +#[contracttype] +enum DataKey { + Key(i32), +} #[contract] pub struct Contract; #[contractimpl] impl Contract { - pub fn store(env: Env, k: i32, v: i32) { - env.storage().persistent().set(&k, &v) + pub fn get_persistent(env: Env, k: i32) -> Option { + env.storage().persistent().get(&DataKey::Key(k)) + } + + pub fn set_persistent(env: Env, k: i32, v: i32) { + env.storage().persistent().set(&DataKey::Key(k), &v); + } + + pub fn get_temporary(env: Env, k: i32) -> Option { + env.storage().temporary().get(&DataKey::Key(k)) + } + + pub fn set_temporary(env: Env, k: i32, v: i32) { + env.storage().temporary().set(&DataKey::Key(k), &v); + } + + pub fn get_instance(env: Env, k: i32) -> Option { + env.storage().instance().get(&DataKey::Key(k)) + } + + pub fn set_instance(env: Env, k: i32, v: i32) { + env.storage().instance().set(&DataKey::Key(k), &v); + } + + pub fn bump_persistent(env: Env, k: i32) { + env.storage().persistent().bump(&DataKey::Key(k), 100); + } + + pub fn bump_temporary(env: Env, k: i32) { + env.storage().temporary().bump(&DataKey::Key(k), 100); + } + + pub fn bump_instance(env: Env) { + env.storage().instance().bump(100); } } @@ -17,10 +54,55 @@ fn test_storage() { let contract_id = e.register_contract(None, Contract); let client = ContractClient::new(&e, &contract_id); - client.store(&2, &4); + // Smoke test instance bump before putting any data into it. + client.bump_instance(); + assert!(client.get_persistent(&11).is_none()); + assert!(client.get_temporary(&11).is_none()); + assert!(client.get_instance(&11).is_none()); + + // Setup the the storage with some values. + e.as_contract(&contract_id, || { + e.storage().persistent().set(&DataKey::Key(11), &1111_i32); + e.storage().temporary().set(&DataKey::Key(11), &2222_i32); + e.storage().instance().set(&DataKey::Key(11), &3333_i32); + }); + assert_eq!(client.get_persistent(&11), Some(1111)); + assert_eq!(client.get_temporary(&11), Some(2222)); + assert_eq!(client.get_instance(&11), Some(3333)); + + client.set_persistent(&22, &111); + assert_eq!(client.get_persistent(&22), Some(111)); assert_eq!( - e.as_contract(&contract_id, || e.storage().persistent().get::<_, i32>(&2)), - Some(4) + e.as_contract(&contract_id, || e + .storage() + .persistent() + .get(&DataKey::Key(22))), + Some(111_i32) ); + + client.set_temporary(&22, &222); + assert_eq!(client.get_temporary(&22), Some(222)); + assert_eq!( + e.as_contract(&contract_id, || e + .storage() + .temporary() + .get(&DataKey::Key(22))), + Some(222_i32) + ); + + client.set_instance(&22, &333); + assert_eq!(client.get_instance(&22), Some(333)); + assert_eq!( + e.as_contract(&contract_id, || e + .storage() + .instance() + .get(&DataKey::Key(22))), + Some(333_i32) + ); + + // Smoke test temp/persistent bumps. This can be enhanced when we provided + // expiration ledger getter for tests. + client.bump_persistent(&11); + client.bump_temporary(&11); }