diff --git a/soroban-env-host/src/auth.rs b/soroban-env-host/src/auth.rs index 93114898d..8e71f11fe 100644 --- a/soroban-env-host/src/auth.rs +++ b/soroban-env-host/src/auth.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::rc::Rc; use soroban_env_common::xdr::{ ContractAuth, ContractDataEntry, HashIdPreimage, HashIdPreimageContractAuth, LedgerEntry, @@ -894,23 +895,24 @@ impl Host { contract_id.metered_clone(self.budget_ref())?, nonce_key_scval, ); - let curr_nonce: u64 = - if self.with_mut_storage(|storage| storage.has(&nonce_key, self.budget_ref()))? { - let sc_val = self.with_mut_storage(|storage| { - match storage.get(&nonce_key, self.budget_ref())?.data { - LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => Ok(val), - _ => Err(self.err_general("unexpected missing nonce entry")), - } - })?; - match sc_val { - ScVal::Object(Some(ScObject::U64(val))) => val, + let curr_nonce: u64 = if self + .with_mut_storage(|storage| storage.has(Rc::clone(&nonce_key), self.budget_ref()))? + { + let entry = self.with_mut_storage(|storage| { + storage.get(Rc::clone(&nonce_key), self.budget_ref()) + })?; + match &entry.data { + LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => match val { + ScVal::Object(Some(ScObject::U64(val))) => val.clone(), _ => { return Err(self.err_general("unexpected nonce entry type")); } - } - } else { - 0 - }; + }, + _ => return Err(self.err_general("unexpected missing nonce entry")), + } + } else { + 0 + }; Ok(curr_nonce) } @@ -926,23 +928,24 @@ impl Host { contract_id.metered_clone(self.budget_ref())?, nonce_key_scval.clone(), ); - let curr_nonce: u64 = - if self.with_mut_storage(|storage| storage.has(&nonce_key, self.budget_ref()))? { - let sc_val = self.with_mut_storage(|storage| { - match storage.get(&nonce_key, self.budget_ref())?.data { - LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => Ok(val), - _ => Err(self.err_general("unexpected missing nonce entry")), - } - })?; - match sc_val { - ScVal::Object(Some(ScObject::U64(val))) => val, + let curr_nonce: u64 = if self + .with_mut_storage(|storage| storage.has(Rc::clone(&nonce_key), self.budget_ref()))? + { + let entry = self.with_mut_storage(|storage| { + storage.get(Rc::clone(&nonce_key), self.budget_ref()) + })?; + match &entry.data { + LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => match val { + ScVal::Object(Some(ScObject::U64(val))) => val.clone(), _ => { return Err(self.err_general("unexpected nonce entry type")); } - } - } else { - 0 - }; + }, + _ => return Err(self.err_general("unexpected missing nonce entry")), + } + } else { + 0 + }; let data = LedgerEntryData::ContractData(ContractDataEntry { contract_id: contract_id.metered_clone(self.budget_ref())?, key: nonce_key_scval, @@ -953,7 +956,7 @@ impl Host { data, ext: LedgerEntryExt::V0, }; - self.with_mut_storage(|storage| storage.put(&nonce_key, &entry, self.budget_ref()))?; + self.with_mut_storage(|storage| storage.put(nonce_key, Rc::new(entry), self.budget_ref()))?; Ok(curr_nonce) } } diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 800018cb5..47c1f947c 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -767,13 +767,12 @@ impl Host { contract_source: ScContractCode, ) -> Result<(), HostError> { let new_contract_id = self.hash_from_obj_input("id_obj", contract_id)?; - let storage_key = - self.contract_source_ledger_key(new_contract_id.metered_clone(&self.0.budget)?); + let storage_key = self.contract_source_ledger_key(&new_contract_id)?; if self .0 .storage .borrow_mut() - .has(&storage_key, self.as_budget())? + .has(Rc::clone(&storage_key), self.as_budget())? { return Err(self.err_general("Contract already exists")); } @@ -781,18 +780,17 @@ impl Host { // without this check it would be possible to accidentally create a // contract that never may be invoked (just by providing a bad hash). if let ScContractCode::WasmRef(wasm_hash) = &contract_source { - let wasm_storage_key = - self.contract_code_ledger_key(wasm_hash.metered_clone(&self.0.budget)?); + let wasm_storage_key = self.contract_code_ledger_key(wasm_hash)?; if !self .0 .storage .borrow_mut() - .has(&wasm_storage_key, self.as_budget())? + .has(wasm_storage_key, self.as_budget())? { return Err(self.err_general("Contract code was not installed")); } } - self.store_contract_source(contract_source, new_contract_id, &storage_key)?; + self.store_contract_source(contract_source, new_contract_id, storage_key)?; Ok(()) } @@ -842,11 +840,18 @@ impl Host { args: &[RawVal], ) -> Result { // Create key for storage - let storage_key = self.contract_source_ledger_key(id.metered_clone(&self.0.budget)?); - match self.retrieve_contract_source_from_storage(&storage_key)? { + let storage_key = self.contract_source_ledger_key(id)?; + match self.retrieve_contract_source_from_storage(storage_key)? { #[cfg(feature = "vm")] ScContractCode::WasmRef(wasm_hash) => { - let code_entry = self.retrieve_contract_code_from_storage(wasm_hash)?; + let key = self.contract_code_ledger_key(&wasm_hash)?; + let entry = &self.0.storage.borrow_mut().get(key, self.as_budget())?; + let code_entry = if let LedgerEntryData::ContractCode(e) = &entry.data { + Ok(e) + } else { + Err(self.err_status(ScHostStorageErrorCode::AccessToUnknownEntry)) + }?; + let vm = Vm::new( self, id.metered_clone(&self.0.budget)?, @@ -1053,10 +1058,10 @@ impl Host { #[cfg(any(test, feature = "testutils"))] pub fn add_ledger_entry( &self, - key: LedgerKey, - val: soroban_env_common::xdr::LedgerEntry, + key: Rc, + val: Rc, ) -> Result<(), HostError> { - self.with_mut_storage(|storage| storage.put(&key, &val, self.as_budget())) + self.with_mut_storage(|storage| storage.put(key, val, self.as_budget())) } // Returns the top-level authorizations that have been recorded for the last @@ -1114,14 +1119,14 @@ impl Host { fn install_contract(&self, args: InstallContractCodeArgs) -> Result { let hash_bytes = self.metered_hash_xdr(&args)?; let hash_obj = self.add_host_object(hash_bytes.to_vec())?; - let code_key = LedgerKey::ContractCode(LedgerKeyContractCode { + let code_key = Rc::new(LedgerKey::ContractCode(LedgerKeyContractCode { hash: Hash(hash_bytes.metered_clone(self.budget_ref())?), - }); + })); if !self .0 .storage .borrow_mut() - .has(&code_key, self.as_budget())? + .has(Rc::clone(&code_key), self.as_budget())? { self.with_mut_storage(|storage| { let data = LedgerEntryData::ContractCode(ContractCodeEntry { @@ -1130,8 +1135,8 @@ impl Host { ext: ExtensionPoint::V0, }); storage.put( - &code_key, - &Host::ledger_entry_from_data(data), + code_key, + Host::ledger_entry_from_data(data), self.as_budget(), ) })?; @@ -1850,8 +1855,8 @@ impl VmCallerEnv for Host { val: self.from_host_val(v)?, }); self.0.storage.borrow_mut().put( - &key, - &Host::ledger_entry_from_data(data), + key, + Host::ledger_entry_from_data(data), self.as_budget(), )?; Ok(().into()) @@ -1864,7 +1869,7 @@ impl VmCallerEnv for Host { k: RawVal, ) -> Result { let key = self.storage_key_from_rawval(k)?; - let res = self.0.storage.borrow_mut().has(&key, self.as_budget())?; + let res = self.0.storage.borrow_mut().has(key, self.as_budget())?; Ok(RawVal::from_bool(res)) } @@ -1875,13 +1880,12 @@ impl VmCallerEnv for Host { k: RawVal, ) -> Result { let key = self.storage_key_from_rawval(k)?; - match self + let entry = self .0 .storage .borrow_mut() - .get(&key, self.as_budget())? - .data - { + .get(Rc::clone(&key), self.as_budget())?; + match &entry.data { LedgerEntryData::ContractData(ContractDataEntry { contract_id, key, @@ -1901,7 +1905,7 @@ impl VmCallerEnv for Host { k: RawVal, ) -> Result { let key = self.contract_data_key_from_rawval(k)?; - self.0.storage.borrow_mut().del(&key, self.as_budget())?; + self.0.storage.borrow_mut().del(key, self.as_budget())?; Ok(().into()) } diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs index fa47b1e89..4d944cefb 100644 --- a/soroban-env-host/src/host/conversion.rs +++ b/soroban-env-host/src/host/conversion.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use super::metered_clone::MeteredClone; use crate::xdr::{ Hash, LedgerKey, LedgerKeyContractData, ScHostFnErrorCode, ScHostObjErrorCode, @@ -192,26 +194,29 @@ impl Host { /// Converts a [`RawVal`] to an [`ScVal`] and combines it with the currently-executing /// [`ContractID`] to produce a [`Key`], that can be used to access ledger [`Storage`]. // Notes on metering: covered by components. - pub fn storage_key_from_rawval(&self, k: RawVal) -> Result { - Ok(LedgerKey::ContractData(LedgerKeyContractData { + pub fn storage_key_from_rawval(&self, k: RawVal) -> Result, HostError> { + Ok(Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id: self.get_current_contract_id_internal()?, key: self.from_host_val(k)?, - })) + }))) } - pub(crate) fn storage_key_for_contract(&self, contract_id: Hash, key: ScVal) -> LedgerKey { - LedgerKey::ContractData(LedgerKeyContractData { contract_id, key }) + pub(crate) fn storage_key_for_contract(&self, contract_id: Hash, key: ScVal) -> Rc { + Rc::new(LedgerKey::ContractData(LedgerKeyContractData { + contract_id, + key, + })) } - pub fn storage_key_from_scval(&self, key: ScVal) -> Result { - Ok(LedgerKey::ContractData(LedgerKeyContractData { + pub fn storage_key_from_scval(&self, key: ScVal) -> Result, HostError> { + Ok(Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id: self.get_current_contract_id_internal()?, key, - })) + }))) } // Notes on metering: covered by components. - pub fn contract_data_key_from_rawval(&self, k: RawVal) -> Result { + pub fn contract_data_key_from_rawval(&self, k: RawVal) -> Result, HostError> { let key_scval = self.from_host_val(k)?; match &key_scval { ScVal::Static(ScStatic::LedgerKeyContractCode) => { @@ -228,7 +233,6 @@ impl Host { } _ => (), }; - self.storage_key_from_scval(key_scval) } diff --git a/soroban-env-host/src/host/data_helper.rs b/soroban-env-host/src/host/data_helper.rs index 775f90fe1..108cac044 100644 --- a/soroban-env-host/src/host/data_helper.rs +++ b/soroban-env-host/src/host/data_helper.rs @@ -1,10 +1,11 @@ use core::cmp::min; +use std::rc::Rc; use soroban_env_common::{Env, InvokerType}; use crate::budget::AsBudget; use crate::xdr::{ - AccountEntry, AccountId, Asset, ContractCodeEntry, ContractDataEntry, Hash, HashIdPreimage, + AccountEntry, AccountId, Asset, ContractDataEntry, Hash, HashIdPreimage, HashIdPreimageContractId, HashIdPreimageCreateContractArgs, HashIdPreimageEd25519ContractId, HashIdPreimageFromAsset, HashIdPreimageSourceAccountContractId, LedgerEntry, LedgerEntryData, LedgerEntryExt, LedgerKey, LedgerKeyAccount, LedgerKeyContractCode, LedgerKeyContractData, @@ -17,52 +18,43 @@ use super::metered_clone::MeteredClone; impl Host { // Notes on metering: free - pub fn contract_source_ledger_key(&self, contract_id: Hash) -> LedgerKey { - LedgerKey::ContractData(LedgerKeyContractData { + pub fn contract_source_ledger_key( + &self, + contract_id: &Hash, + ) -> Result, HostError> { + let contract_id = contract_id.metered_clone(self.as_budget())?; + Ok(Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id, key: ScVal::Static(ScStatic::LedgerKeyContractCode), - }) + }))) } // Notes on metering: retrieving from storage covered. Rest are free. pub(crate) fn retrieve_contract_source_from_storage( &self, - key: &LedgerKey, + key: Rc, ) -> Result { - let scval = match self.0.storage.borrow_mut().get(key, self.as_budget())?.data { - LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => Ok(val), - _ => Err(self.err_status(ScHostStorageErrorCode::ExpectContractData)), - }?; - match scval { - ScVal::Object(Some(ScObject::ContractCode(code))) => Ok(code), - _ => { - return Err(self.err_status_msg( + let entry = self.0.storage.borrow_mut().get(key, self.as_budget())?; + match &entry.data { + LedgerEntryData::ContractData(ContractDataEntry { val, .. }) => match val { + ScVal::Object(Some(ScObject::ContractCode(code))) => Ok(code.clone()), + _ => Err(self.err_status_msg( ScHostValErrorCode::UnexpectedValType, "ledger entry for contract code does not contain contract code", - )) - } + )), + }, + _ => Err(self.err_status(ScHostStorageErrorCode::ExpectContractData)), } } - pub(crate) fn contract_code_ledger_key(&self, wasm_hash: Hash) -> LedgerKey { - LedgerKey::ContractCode(LedgerKeyContractCode { hash: wasm_hash }) - } - - pub(crate) fn retrieve_contract_code_from_storage( + pub(crate) fn contract_code_ledger_key( &self, - wasm_hash: Hash, - ) -> Result { - let key = self.contract_code_ledger_key(wasm_hash); - match self - .0 - .storage - .borrow_mut() - .get(&key, self.as_budget())? - .data - { - LedgerEntryData::ContractCode(e) => Ok(e), - _ => Err(self.err_status(ScHostStorageErrorCode::AccessToUnknownEntry)), - } + wasm_hash: &Hash, + ) -> Result, HostError> { + let wasm_hash = wasm_hash.metered_clone(self.as_budget())?; + Ok(Rc::new(LedgerKey::ContractCode(LedgerKeyContractCode { + hash: wasm_hash, + }))) } // Notes on metering: `from_host_obj` and `put` to storage covered, rest are free. @@ -70,7 +62,7 @@ impl Host { &self, contract_source: ScContractCode, contract_id: Hash, - key: &LedgerKey, + key: Rc, ) -> Result<(), HostError> { let data = LedgerEntryData::ContractData(ContractDataEntry { contract_id, @@ -78,8 +70,8 @@ impl Host { val: ScVal::Object(Some(ScObject::ContractCode(contract_source))), }); self.0.storage.borrow_mut().put( - &key, - &Host::ledger_entry_from_data(data), + key, + Host::ledger_entry_from_data(data), self.as_budget(), )?; Ok(()) @@ -169,14 +161,14 @@ impl Host { // notes on metering: `get` from storage is covered. Rest are free. pub fn load_account(&self, account_id: AccountId) -> Result { let acc = self.to_account_key(account_id); - self.with_mut_storage(|storage| match storage.get(&acc, self.as_budget())?.data { - LedgerEntryData::Account(ae) => Ok(ae), + self.with_mut_storage(|storage| match &storage.get(acc, self.as_budget())?.data { + LedgerEntryData::Account(ae) => Ok(ae.clone()), // TODO: clone needs to be metered _ => Err(self.err_general("not account")), }) } - pub(crate) fn to_account_key(&self, account_id: AccountId) -> LedgerKey { - LedgerKey::Account(LedgerKeyAccount { account_id }) + pub(crate) fn to_account_key(&self, account_id: AccountId) -> Rc { + Rc::new(LedgerKey::Account(LedgerKeyAccount { account_id })) } pub(crate) fn create_asset_4(&self, asset_code: [u8; 4], issuer: AccountId) -> TrustLineAsset { @@ -203,8 +195,11 @@ impl Host { &self, account_id: AccountId, asset: TrustLineAsset, - ) -> LedgerKey { - LedgerKey::Trustline(LedgerKeyTrustLine { account_id, asset }) + ) -> Rc { + Rc::new(LedgerKey::Trustline(LedgerKeyTrustLine { + account_id, + asset, + })) } pub(crate) fn get_signer_weight_from_account( @@ -243,13 +238,13 @@ impl Host { } } - pub(crate) fn ledger_entry_from_data(data: LedgerEntryData) -> LedgerEntry { - LedgerEntry { + pub(crate) fn ledger_entry_from_data(data: LedgerEntryData) -> Rc { + Rc::new(LedgerEntry { // This is modified to the appropriate value on the core side during // commiting the ledger transaction. last_modified_ledger_seq: 0, data, ext: LedgerEntryExt::V0, - } + }) } } diff --git a/soroban-env-host/src/host/metered_clone.rs b/soroban-env-host/src/host/metered_clone.rs index 6d73aef40..0bd17b4be 100644 --- a/soroban-env-host/src/host/metered_clone.rs +++ b/soroban-env-host/src/host/metered_clone.rs @@ -2,8 +2,7 @@ use std::{mem, rc::Rc}; use soroban_env_common::{ xdr::{ - BytesM, ContractEvent, ContractEventBody, LedgerEntry, LedgerKey, ScAddress, ScMap, - ScMapEntry, ScObject, ScVal, + BytesM, ContractEvent, ContractEventBody, ScAddress, ScMap, ScMapEntry, ScObject, ScVal, }, RawVal, }; @@ -140,17 +139,6 @@ where const ELT_SIZE: u64 = ::ELT_SIZE + ::ELT_SIZE; } -// TODO: this isn't correct: these two have substructure to account for; -// probably they should never be cloned in the middle of a contract at all (the -// storage maps are Rc<> now) but changing that means changing the storage -// interface. See https://github.com/stellar/rs-soroban-env/issues/603 -impl MeteredClone for LedgerKey { - const ELT_SIZE: u64 = 80; -} -impl MeteredClone for LedgerEntry { - const ELT_SIZE: u64 = 264; -} - impl MeteredClone for [C; N] { const IS_SHALLOW: bool = C::IS_SHALLOW; @@ -366,8 +354,6 @@ mod test { expect!["33"].assert_eq(std::mem::size_of::().to_string().as_str()); expect!["32"].assert_eq(std::mem::size_of::().to_string().as_str()); expect!["33"].assert_eq(std::mem::size_of::().to_string().as_str()); - expect!["80"].assert_eq(std::mem::size_of::().to_string().as_str()); - expect!["264"].assert_eq(std::mem::size_of::().to_string().as_str()); expect!["40"].assert_eq(std::mem::size_of::().to_string().as_str()); expect!["24"].assert_eq(std::mem::size_of::().to_string().as_str()); expect!["80"].assert_eq(std::mem::size_of::().to_string().as_str()); @@ -405,8 +391,6 @@ mod test { assert_mem_size_equals_elt_size!(ScContractCode); assert_mem_size_equals_elt_size!(Uint256); assert_mem_size_equals_elt_size!(ScAddress); - assert_mem_size_equals_elt_size!(LedgerKey); - assert_mem_size_equals_elt_size!(LedgerEntry); assert_mem_size_equals_elt_size!(ScVal); assert_mem_size_equals_elt_size!(ScVec); assert_mem_size_equals_elt_size!(ScMapEntry); diff --git a/soroban-env-host/src/native_contract/testutils.rs b/soroban-env-host/src/native_contract/testutils.rs index f743928f2..e59ee3b09 100644 --- a/soroban-env-host/src/native_contract/testutils.rs +++ b/soroban-env-host/src/native_contract/testutils.rs @@ -273,7 +273,7 @@ pub(crate) fn create_account( flags: u32, ) { let key = host.to_account_key(account_id.clone().into()); - let account_id = match &key { + let account_id = match key.as_ref() { LedgerKey::Account(acc) => acc.account_id.clone(), _ => unreachable!(), }; @@ -310,7 +310,7 @@ pub(crate) fn create_account( }; let acc_entry = AccountEntry { account_id, - balance: balance, + balance, seq_num: SequenceNumber(0), num_sub_entries, inflation_dest: None, diff --git a/soroban-env-host/src/native_contract/token/balance.rs b/soroban-env-host/src/native_contract/token/balance.rs index f959ce909..84eea916e 100644 --- a/soroban-env-host/src/native_contract/token/balance.rs +++ b/soroban-env-host/src/native_contract/token/balance.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::budget::AsBudget; use crate::host::Host; use crate::native_contract::base_types::Address; @@ -343,15 +345,15 @@ fn transfer_account_balance(e: &Host, account_id: AccountId, amount: i64) -> Res e.with_mut_storage(|storage| { let mut le = storage - .get(&lk, e.as_budget()) + .get(Rc::clone(&lk), e.as_budget()) .map_err(|_| e.err_status_msg(ContractError::AccountMissingError, "account missing"))?; - let ae = match &mut le.data { - LedgerEntryData::Account(ae) => Ok(ae), + let mut ae = match &le.data { + LedgerEntryData::Account(ae) => Ok(ae.clone()), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; - let (min_balance, max_balance) = get_min_max_account_balance(e, ae)?; + let (min_balance, max_balance) = get_min_max_account_balance(e, &ae)?; let new_balance = if amount <= 0 { ae.balance + amount @@ -362,7 +364,8 @@ fn transfer_account_balance(e: &Host, account_id: AccountId, amount: i64) -> Res }; if new_balance >= min_balance && new_balance <= max_balance { ae.balance = new_balance; - storage.put(&lk, &le, e.as_budget()) + le = Host::ledger_entry_from_data(LedgerEntryData::Account(ae)); + storage.put(lk, le, e.as_budget()) } else { Err(err!( e, @@ -385,12 +388,12 @@ fn transfer_trustline_balance( ) -> Result<(), HostError> { let lk = e.to_trustline_key(account_id, asset); e.with_mut_storage(|storage| { - let mut le = storage.get(&lk, e.as_budget()).map_err(|_| { + let mut le = storage.get(Rc::clone(&lk), e.as_budget()).map_err(|_| { e.err_status_msg(ContractError::TrustlineMissingError, "trustline missing") })?; - let tl = match &mut le.data { - LedgerEntryData::Trustline(tl) => Ok(tl), + let mut tl = match &le.data { + LedgerEntryData::Trustline(tl) => Ok(tl.clone()), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; @@ -405,7 +408,8 @@ fn transfer_trustline_balance( }; if new_balance >= min_balance && new_balance <= max_balance { tl.balance = new_balance; - storage.put(&lk, &le, e.as_budget()) + le = Host::ledger_entry_from_data(LedgerEntryData::Trustline(tl)); + storage.put(lk, le, e.as_budget()) } else { Err(err!( e, @@ -426,10 +430,10 @@ fn get_account_balance(e: &Host, account_id: AccountId) -> Result<(i64, i64), Ho e.with_mut_storage(|storage| { let le = storage - .get(&lk, e.as_budget()) + .get(lk, e.as_budget()) .map_err(|_| e.err_status_msg(ContractError::AccountMissingError, "account missing"))?; - let ae = match le.data { + let ae = match &le.data { LedgerEntryData::Account(ae) => Ok(ae), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; @@ -479,16 +483,16 @@ fn get_trustline_balance( ) -> Result<(i64, i64), HostError> { let lk = e.to_trustline_key(account_id, asset); e.with_mut_storage(|storage| { - let mut le = storage.get(&lk, e.as_budget()).map_err(|_| { + let le = storage.get(lk, e.as_budget()).map_err(|_| { e.err_status_msg(ContractError::TrustlineMissingError, "trustline missing") })?; - let tl = match &mut le.data { - LedgerEntryData::Trustline(tl) => Ok(tl), + let tl = match &le.data { + LedgerEntryData::Trustline(tl) => Ok(tl.clone()), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; - let min = get_min_max_trustline_balance(e, tl)?.0; + let min = get_min_max_trustline_balance(e, &tl)?.0; if tl.balance < min { return Err(e.err_status_msg( ContractError::InternalError, @@ -561,11 +565,11 @@ fn get_trustline_flags( ) -> Result { let lk = e.to_trustline_key(account_id, asset); e.with_mut_storage(|storage| { - let le = storage.get(&lk, e.as_budget()).map_err(|_| { + let le = storage.get(lk, e.as_budget()).map_err(|_| { e.err_status_msg(ContractError::TrustlineMissingError, "trustline missing") })?; - let tl = match le.data { + let tl = match &le.data { LedgerEntryData::Trustline(tl) => Ok(tl), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; @@ -641,12 +645,12 @@ fn set_trustline_authorization( ) -> Result<(), HostError> { let lk = e.to_trustline_key(account_id, asset); e.with_mut_storage(|storage| { - let mut le = storage.get(&lk, e.as_budget()).map_err(|_| { + let mut le = storage.get(Rc::clone(&lk), e.as_budget()).map_err(|_| { e.err_status_msg(ContractError::TrustlineMissingError, "trustline missing") })?; - let tl = match &mut le.data { - LedgerEntryData::Trustline(tl) => Ok(tl), + let mut tl = match &le.data { + LedgerEntryData::Trustline(tl) => Ok(tl.clone()), _ => Err(e.err_status_msg(ContractError::InternalError, "unexpected entry found")), }?; @@ -659,7 +663,8 @@ fn set_trustline_authorization( tl.flags &= !(TrustLineFlags::AuthorizedFlag as u32); tl.flags |= TrustLineFlags::AuthorizedToMaintainLiabilitiesFlag as u32; } - storage.put(&lk, &le, e.as_budget()) + le = Host::ledger_entry_from_data(LedgerEntryData::Trustline(tl)); + storage.put(lk, le, e.as_budget()) }) } diff --git a/soroban-env-host/src/storage.rs b/soroban-env-host/src/storage.rs index 0b18b65ba..3dba374de 100644 --- a/soroban-env-host/src/storage.rs +++ b/soroban-env-host/src/storage.rs @@ -12,7 +12,6 @@ use std::rc::Rc; use soroban_env_common::Compare; use crate::budget::Budget; -use crate::host::metered_clone::MeteredClone; use crate::xdr::{LedgerEntry, LedgerKey, ScHostStorageErrorCode}; use crate::Host; use crate::{host::metered_map::MeteredOrdMap, HostError}; @@ -52,8 +51,8 @@ impl Compare for Budget { /// A helper type used by [FootprintMode::Recording] to provide access /// to a stable read-snapshot of a ledger. pub trait SnapshotSource { - fn get(&self, key: &LedgerKey) -> Result; - fn has(&self, key: &LedgerKey) -> Result; + fn get(&self, key: &Rc) -> Result, HostError>; + fn has(&self, key: &Rc) -> Result; } /// Describes the total set of [LedgerKey]s that a given transaction @@ -71,39 +70,35 @@ pub struct Footprint(pub FootprintMap); impl Footprint { pub fn record_access( &mut self, - key: &LedgerKey, + key: Rc, ty: AccessType, budget: &Budget, ) -> Result<(), HostError> { - if let Some(existing) = self.0.get::(key, budget)? { + if let Some(existing) = self.0.get::>(&key, budget)? { match (existing, ty.clone()) { (AccessType::ReadOnly, AccessType::ReadOnly) => Ok(()), (AccessType::ReadOnly, AccessType::ReadWrite) => { // The only interesting case is an upgrade // from previously-read-only to read-write. - self.0 = self - .0 - .insert(Rc::new(key.metered_clone(budget)?), ty, budget)?; + self.0 = self.0.insert(key, ty, budget)?; Ok(()) } (AccessType::ReadWrite, AccessType::ReadOnly) => Ok(()), (AccessType::ReadWrite, AccessType::ReadWrite) => Ok(()), } } else { - self.0 = self - .0 - .insert(Rc::new(key.metered_clone(budget)?), ty, budget)?; + self.0 = self.0.insert(key, ty, budget)?; Ok(()) } } pub fn enforce_access( &mut self, - key: &LedgerKey, + key: &Rc, ty: AccessType, budget: &Budget, ) -> Result<(), HostError> { - if let Some(existing) = self.0.get::(key, budget)? { + if let Some(existing) = self.0.get::>(key, budget)? { match (existing, ty) { (AccessType::ReadOnly, AccessType::ReadOnly) => Ok(()), (AccessType::ReadOnly, AccessType::ReadWrite) => { @@ -185,52 +180,50 @@ impl Storage { /// /// In [FootprintMode::Enforcing] mode, succeeds only if the read /// [LedgerKey] has been declared in the [Footprint]. - pub fn get(&mut self, key: &LedgerKey, budget: &Budget) -> Result { + pub fn get( + &mut self, + key: Rc, + budget: &Budget, + ) -> Result, HostError> { let ty = AccessType::ReadOnly; match self.mode { FootprintMode::Recording(ref src) => { - self.footprint.record_access(key, ty, budget)?; + self.footprint.record_access(Rc::clone(&key), ty, budget)?; // In recording mode we treat the map as a cache // that misses read-through to the underlying src. - if !self.map.contains_key::(key, budget)? { - self.map = self.map.insert( - Rc::new(key.metered_clone(budget)?), - Some(Rc::new(src.get(key)?)), - budget, - )?; + if !self.map.contains_key::>(&key, budget)? { + self.map = self + .map + .insert(Rc::clone(&key), Some(src.get(&key)?), budget)?; } } FootprintMode::Enforcing => { - self.footprint.enforce_access(key, ty, budget)?; + self.footprint.enforce_access(&key, ty, budget)?; } }; - match self.map.get::(key, budget)? { + match self.map.get::>(&key, budget)? { None => Err(ScHostStorageErrorCode::MissingKeyInGet.into()), Some(None) => Err(ScHostStorageErrorCode::GetOnDeletedKey.into()), - Some(Some(val)) => Ok((**val).metered_clone(budget)?), + Some(Some(val)) => Ok(Rc::clone(&val)), } } fn put_opt( &mut self, - key: &LedgerKey, - val: Option, + key: Rc, + val: Option>, budget: &Budget, ) -> Result<(), HostError> { let ty = AccessType::ReadWrite; match self.mode { FootprintMode::Recording(_) => { - self.footprint.record_access(key, ty, budget)?; + self.footprint.record_access(Rc::clone(&key), ty, budget)?; } FootprintMode::Enforcing => { - self.footprint.enforce_access(key, ty, budget)?; + self.footprint.enforce_access(&key, ty, budget)?; } }; - self.map = self.map.insert( - Rc::new(key.metered_clone(budget)?), - val.map(|v| Rc::new(v)), - budget, - )?; + self.map = self.map.insert(key, val, budget)?; Ok(()) } @@ -245,11 +238,11 @@ impl Storage { /// [AccessType::ReadWrite]. pub fn put( &mut self, - key: &LedgerKey, - val: &LedgerEntry, + key: Rc, + val: Rc, budget: &Budget, ) -> Result<(), HostError> { - self.put_opt(key, Some(val.metered_clone(budget)?), budget) + self.put_opt(key, Some(val), budget) } /// Attempts to delete the [LedgerEntry] associated with a given [LedgerKey] @@ -261,7 +254,7 @@ impl Storage { /// In [FootprintMode::Enforcing] mode, succeeds only if the deleted /// [LedgerKey] has been declared in the [Footprint] as /// [AccessType::ReadWrite]. - pub fn del(&mut self, key: &LedgerKey, budget: &Budget) -> Result<(), HostError> { + pub fn del(&mut self, key: Rc, budget: &Budget) -> Result<(), HostError> { self.put_opt(key, None, budget) } @@ -274,22 +267,22 @@ impl Storage { /// /// In [FootprintMode::Enforcing] mode, succeeds only if the access has been /// declared in the [Footprint]. - pub fn has(&mut self, key: &LedgerKey, budget: &Budget) -> Result { + pub fn has(&mut self, key: Rc, budget: &Budget) -> Result { let ty = AccessType::ReadOnly; match self.mode { FootprintMode::Recording(ref src) => { - self.footprint.record_access(key, ty, budget)?; + self.footprint.record_access(Rc::clone(&key), ty, budget)?; // We don't cache has() calls but we do // consult the cache before answering them. - match self.map.get::(key, budget)? { + match self.map.get::>(&key, budget)? { Some(None) => Ok(false), Some(Some(_)) => Ok(true), - None => src.has(key), + None => src.has(&key), } } FootprintMode::Enforcing => { - self.footprint.enforce_access(key, ty, budget)?; - match self.map.get::(key, budget)? { + self.footprint.enforce_access(&key, ty, budget)?; + match self.map.get::>(&key, budget)? { Some(None) => Ok(false), Some(Some(_)) => Ok(true), None => Ok(false), @@ -313,23 +306,23 @@ mod test_footprint { let mut fp = Footprint::default(); // record when key not exist let contract_id = [0; 32].into(); - let key = LedgerKey::ContractData(LedgerKeyContractData { + let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id, key: ScVal::I32(0), - }); - fp.record_access(&key, AccessType::ReadOnly, &budget)?; + })); + fp.record_access(Rc::clone(&key), AccessType::ReadOnly, &budget)?; assert_eq!(fp.0.contains_key::(&key, &budget)?, true); assert_eq!( fp.0.get::(&key, &budget)?, Some(&AccessType::ReadOnly) ); // record and change access - fp.record_access(&key, AccessType::ReadWrite, &budget)?; + fp.record_access(Rc::clone(&key), AccessType::ReadWrite, &budget)?; assert_eq!( fp.0.get::(&key, &budget)?, Some(&AccessType::ReadWrite) ); - fp.record_access(&key, AccessType::ReadOnly, &budget)?; + fp.record_access(Rc::clone(&key), AccessType::ReadOnly, &budget)?; assert_eq!( fp.0.get::(&key, &budget)?, Some(&AccessType::ReadWrite) @@ -341,16 +334,16 @@ mod test_footprint { fn footprint_enforce_access() -> Result<(), HostError> { let budget = Budget::default(); let contract_id = [0; 32].into(); - let key = LedgerKey::ContractData(LedgerKeyContractData { + let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id, key: ScVal::I32(0), - }); - let om = [(Rc::new(key.clone()), AccessType::ReadOnly)].into(); + })); + let om = [(Rc::clone(&key), AccessType::ReadOnly)].into(); let mom = MeteredOrdMap::from_map(om, &budget)?; let mut fp = Footprint(mom); fp.enforce_access(&key, AccessType::ReadOnly, &budget)?; fp.0 = - fp.0.insert(Rc::new(key.clone()), AccessType::ReadWrite, &budget)?; + fp.0.insert(Rc::clone(&key), AccessType::ReadWrite, &budget)?; fp.enforce_access(&key, AccessType::ReadOnly, &budget)?; fp.enforce_access(&key, AccessType::ReadWrite, &budget)?; Ok(()) @@ -361,10 +354,10 @@ mod test_footprint { let budget = Budget::default(); let mut fp = Footprint::default(); let contract_id = [0; 32].into(); - let key = LedgerKey::ContractData(LedgerKeyContractData { + let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id, key: ScVal::I32(0), - }); + })); let res = fp.enforce_access(&key, AccessType::ReadOnly, &budget); assert!(HostError::result_matches_err_status( res, @@ -377,11 +370,11 @@ mod test_footprint { fn footprint_attempt_to_write_readonly_entry() -> Result<(), HostError> { let budget = Budget::default(); let contract_id = [0; 32].into(); - let key = LedgerKey::ContractData(LedgerKeyContractData { + let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData { contract_id, key: ScVal::I32(0), - }); - let om = [(Rc::new(key.clone()), AccessType::ReadOnly)].into(); + })); + let om = [(Rc::clone(&key), AccessType::ReadOnly)].into(); let mom = MeteredOrdMap::from_map(om, &budget)?; let mut fp = Footprint(mom); let res = fp.enforce_access(&key, AccessType::ReadWrite, &budget); @@ -401,23 +394,23 @@ pub(crate) mod test_storage { use super::*; #[allow(dead_code)] - pub(crate) struct MockSnapshotSource(BTreeMap); + pub(crate) struct MockSnapshotSource(BTreeMap, Rc>); #[allow(dead_code)] impl MockSnapshotSource { pub(crate) fn new() -> Self { - Self(BTreeMap::::new()) + Self(BTreeMap::, Rc>::new()) } } impl SnapshotSource for MockSnapshotSource { - fn get(&self, key: &LedgerKey) -> Result { + fn get(&self, key: &Rc) -> Result, HostError> { if let Some(val) = self.0.get(key) { - Ok(val.clone()) + Ok(Rc::clone(&val)) } else { Err(ScUnknownErrorCode::General.into()) } } - fn has(&self, key: &LedgerKey) -> Result { + fn has(&self, key: &Rc) -> Result { Ok(self.0.contains_key(key)) } } diff --git a/soroban-env-host/src/test/complex.rs b/soroban-env-host/src/test/complex.rs index fd31d83de..bdd8c656e 100644 --- a/soroban-env-host/src/test/complex.rs +++ b/soroban-env-host/src/test/complex.rs @@ -11,11 +11,11 @@ use soroban_test_wasms::COMPLEX; struct EmptySnap; impl SnapshotSource for EmptySnap { - fn get(&self, _key: &LedgerKey) -> Result { + fn get(&self, _key: &Rc) -> Result, HostError> { Err(ScHostStorageErrorCode::AccessToUnknownEntry.into()) } - fn has(&self, _key: &LedgerKey) -> Result { + fn has(&self, _key: &Rc) -> Result { Ok(false) } } diff --git a/soroban-env-host/src/test/lifecycle.rs b/soroban-env-host/src/test/lifecycle.rs index 9a3098a7c..c9856a0eb 100644 --- a/soroban-env-host/src/test/lifecycle.rs +++ b/soroban-env-host/src/test/lifecycle.rs @@ -16,13 +16,15 @@ use sha2::{Digest, Sha256}; use super::util::{generate_account_id, generate_bytes_array}; fn get_contract_wasm_ref(host: &Host, contract_id: Hash) -> Hash { - let storage_key = host.contract_source_ledger_key(contract_id); + let storage_key = host.contract_source_ledger_key(&contract_id).unwrap(); host.with_mut_storage(|s: &mut Storage| { - assert!(s.has(&storage_key, host.as_budget()).unwrap()); + assert!(s.has(storage_key.clone(), host.as_budget()).unwrap()); - match s.get(&storage_key, host.as_budget()).unwrap().data { - LedgerEntryData::ContractData(cde) => match cde.val { - ScVal::Object(Some(ScObject::ContractCode(ScContractCode::WasmRef(h)))) => Ok(h), + match &s.get(storage_key, host.as_budget()).unwrap().data { + LedgerEntryData::ContractData(cde) => match &cde.val { + ScVal::Object(Some(ScObject::ContractCode(ScContractCode::WasmRef(h)))) => { + Ok(h.clone()) + } _ => panic!("expected ScContractCode"), }, _ => panic!("expected contract data"), @@ -32,11 +34,11 @@ fn get_contract_wasm_ref(host: &Host, contract_id: Hash) -> Hash { } fn get_contract_wasm(host: &Host, wasm_hash: Hash) -> Vec { - let storage_key = host.contract_code_ledger_key(wasm_hash); + let storage_key = host.contract_code_ledger_key(&wasm_hash).unwrap(); host.with_mut_storage(|s: &mut Storage| { - assert!(s.has(&storage_key, host.as_budget()).unwrap()); + assert!(s.has(storage_key.clone(), host.as_budget()).unwrap()); - match s.get(&storage_key, host.as_budget()).unwrap().data { + match &s.get(storage_key, host.as_budget()).unwrap().data { LedgerEntryData::ContractCode(code_entry) => Ok(code_entry.code.to_vec()), _ => panic!("expected contract WASM code"), } @@ -94,14 +96,14 @@ fn test_create_contract_from_source_account(host: &Host, code: &[u8]) -> Hash { host.with_mut_storage(|s: &mut Storage| { s.footprint .record_access( - &host.contract_source_ledger_key(contract_id.clone()), + host.contract_source_ledger_key(&contract_id).unwrap(), AccessType::ReadWrite, host.as_budget(), ) .unwrap(); s.footprint .record_access( - &host.contract_code_ledger_key(wasm_hash.clone()), + host.contract_code_ledger_key(&wasm_hash).unwrap(), AccessType::ReadWrite, host.as_budget(), ) @@ -165,14 +167,14 @@ fn create_contract_using_parent_id_test() { host.with_mut_storage(|s: &mut Storage| { s.footprint .record_access( - &host.contract_source_ledger_key(child_id.clone()), + host.contract_source_ledger_key(&child_id).unwrap(), AccessType::ReadWrite, host.as_budget(), ) .unwrap(); s.footprint .record_access( - &host.contract_code_ledger_key(wasm_hash.clone()), + host.contract_code_ledger_key(&wasm_hash).unwrap(), AccessType::ReadWrite, host.as_budget(), ) diff --git a/soroban-env-host/src/test/token.rs b/soroban-env-host/src/test/token.rs index 05f7f68a0..2e8299364 100644 --- a/soroban-env-host/src/test/token.rs +++ b/soroban-env-host/src/test/token.rs @@ -1,4 +1,4 @@ -use std::convert::TryInto; +use std::{convert::TryInto, rc::Rc}; use crate::{ auth::RecordedAuthPayload, @@ -109,7 +109,7 @@ impl TokenTest { ); } - fn create_default_trustline(&self, user: &TestSigner) -> LedgerKey { + fn create_default_trustline(&self, user: &TestSigner) -> Rc { self.create_trustline( &user.account_id(), &keypair_to_account_id(&self.issuer_key), @@ -127,10 +127,10 @@ impl TokenTest { account.balance } - fn get_trustline_balance(&self, key: &LedgerKey) -> i64 { + fn get_trustline_balance(&self, key: Rc) -> i64 { self.host - .with_mut_storage(|s| match s.get(key, self.host.as_budget()).unwrap().data { - LedgerEntryData::Trustline(trustline) => Ok(trustline.balance), + .with_mut_storage(|s| match &s.get(key, self.host.as_budget()).unwrap().data { + LedgerEntryData::Trustline(trustline) => Ok(trustline.balance.clone()), _ => unreachable!(), }) .unwrap() @@ -162,15 +162,19 @@ impl TokenTest { ); } - fn update_account_flags(&self, key: &LedgerKey, new_flags: u32) { + fn update_account_flags(&self, key: Rc, new_flags: u32) { self.host - .with_mut_storage(|s| match s.get(key, self.host.as_budget()).unwrap().data { - LedgerEntryData::Account(mut account) => { - account.flags = new_flags; - let update = Host::ledger_entry_from_data(LedgerEntryData::Account(account)); - s.put(&key, &update, self.host.as_budget()) + .with_mut_storage(|s| { + let entry = s.get(Rc::clone(&key), self.host.as_budget()).unwrap(); + match entry.data.clone() { + LedgerEntryData::Account(mut account) => { + account.flags = new_flags; + let update = + Host::ledger_entry_from_data(LedgerEntryData::Account(account)); + s.put(key, update, self.host.as_budget()) + } + _ => unreachable!(), } - _ => unreachable!(), }) .unwrap(); } @@ -185,7 +189,7 @@ impl TokenTest { flags: u32, // (buying, selling) liabilities liabilities: Option<(i64, i64)>, - ) -> LedgerKey { + ) -> Rc { let asset = match asset_code.len() { 4 => { let mut code = [0_u8; 4]; @@ -231,16 +235,19 @@ impl TokenTest { key } - fn update_trustline_flags(&self, key: &LedgerKey, new_flags: u32) { + fn update_trustline_flags(&self, key: Rc, new_flags: u32) { self.host - .with_mut_storage(|s| match s.get(key, self.host.as_budget()).unwrap().data { - LedgerEntryData::Trustline(mut trustline) => { - trustline.flags = new_flags; - let update = - Host::ledger_entry_from_data(LedgerEntryData::Trustline(trustline)); - s.put(&key, &update, self.host.as_budget()) + .with_mut_storage(|s| { + let entry = s.get(key.clone(), self.host.as_budget()).unwrap(); + match entry.data.clone() { + LedgerEntryData::Trustline(mut trustline) => { + trustline.flags = new_flags; + let update = + Host::ledger_entry_from_data(LedgerEntryData::Trustline(trustline)); + s.put(key, update, self.host.as_budget()) + } + _ => unreachable!(), } - _ => unreachable!(), }) .unwrap(); } @@ -390,7 +397,7 @@ fn test_asset_init(asset_code: &[u8]) { let user = TestSigner::account_with_multisig(&account_id, vec![&test.user_key]); - assert_eq!(test.get_trustline_balance(&trustline_key), 10_000_000); + assert_eq!(test.get_trustline_balance(trustline_key), 10_000_000); assert_eq!(token.balance(user.address(&test.host)).unwrap(), 10_000_000); } @@ -893,7 +900,7 @@ fn test_clawback_on_account() { ); // disable clawback on the trustline - test.update_trustline_flags(&tl_key, 0); + test.update_trustline_flags(Rc::clone(&tl_key), 0); assert_eq!( to_contract_err( token @@ -905,7 +912,7 @@ fn test_clawback_on_account() { ); // enable clawback on the trustline - test.update_trustline_flags(&tl_key, TrustLineFlags::TrustlineClawbackEnabledFlag as u32); + test.update_trustline_flags(tl_key, TrustLineFlags::TrustlineClawbackEnabledFlag as u32); // Clawback everything else token @@ -934,7 +941,7 @@ fn test_clawback_on_contract() { .unwrap(); //disable clawback before minting to user_2 - test.update_account_flags(&issuer_ledger_key, 0); + test.update_account_flags(Rc::clone(&issuer_ledger_key), 0); token .mint(&admin, user_2_addr.clone(), 100_000_000) .unwrap(); @@ -959,7 +966,7 @@ fn test_clawback_on_contract() { ); // enable clawback on the issuer again. Nothing should change for existing balances - test.update_account_flags(&issuer_ledger_key, AccountFlags::ClawbackEnabledFlag as u32); + test.update_account_flags(issuer_ledger_key, AccountFlags::ClawbackEnabledFlag as u32); token .clawback(&admin, user_1_addr.clone(), 40_000_000) @@ -2329,12 +2336,12 @@ fn test_wrapped_asset_classic_balance_boundaries( ) .unwrap(); assert_eq!( - test.get_trustline_balance(&trustline_key), + test.get_trustline_balance(trustline_key.clone()), expected_min_balance ); assert_eq!( - test.get_trustline_balance(&trustline_key2), + test.get_trustline_balance(trustline_key2.clone()), init_balance - expected_min_balance ); @@ -2374,10 +2381,10 @@ fn test_wrapped_asset_classic_balance_boundaries( .unwrap(); assert_eq!( - test.get_trustline_balance(&trustline_key), + test.get_trustline_balance(trustline_key), expected_max_balance ); - assert_eq!(test.get_trustline_balance(&trustline_key2), 0); + assert_eq!(test.get_trustline_balance(trustline_key2), 0); } #[test] @@ -2451,7 +2458,7 @@ fn test_classic_transfers_not_possible_for_unauthorized_asset() { None, ); - assert_eq!(test.get_trustline_balance(&trustline_key), 100_000_000); + assert_eq!(test.get_trustline_balance(trustline_key), 100_000_000); let token = TestToken::new_from_asset( &test.host, @@ -2489,7 +2496,7 @@ fn test_classic_transfers_not_possible_for_unauthorized_asset() { ); // Trustline balance stays the same. - assert_eq!(test.get_trustline_balance(&trustline_key), 100_000_000); + assert_eq!(test.get_trustline_balance(trustline_key), 100_000_000); } #[cfg(feature = "vm")] diff --git a/soroban-env-host/src/test/util.rs b/soroban-env-host/src/test/util.rs index 63e3af46d..03a40c258 100644 --- a/soroban-env-host/src/test/util.rs +++ b/soroban-env-host/src/test/util.rs @@ -87,10 +87,10 @@ impl Host { pub(crate) fn test_account_ledger_key_entry_pair( account_id: AccountId, - ) -> (LedgerKey, LedgerEntry) { - let lk = LedgerKey::Account(xdr::LedgerKeyAccount { + ) -> (Rc, Rc) { + let lk = Rc::new(LedgerKey::Account(xdr::LedgerKeyAccount { account_id: account_id.clone(), - }); + })); let account_entry = AccountEntry { account_id, balance: 100, @@ -103,10 +103,8 @@ impl Host { signers: Default::default(), ext: xdr::AccountEntryExt::V0, }; - ( - lk, - Host::ledger_entry_from_data(LedgerEntryData::Account(account_entry)), - ) + let le = Host::ledger_entry_from_data(LedgerEntryData::Account(account_entry)); + (lk, le) } pub(crate) fn test_scvec(&self, vals: &[T]) -> Result {