1use crate::frame_result::FrameResult;
2use crate::mock_contract::{MockContract, MockContractInner};
3use crate::mock_transaction_handler::MockTransactionHandler;
4use crate::test_environment_builder::TestEnvironmentBuilder;
5use crate::transact_result::TransactResult;
6use crate::transaction_builder::TransactionBuilder;
7use core::ffi::c_void;
8use sha2::{Digest, Sha256};
9use std::cell::RefCell;
10use std::rc::Rc;
11use thruput_storage_cost::StorageCostModel;
12use thruput_sys::FFIErrorCode;
13use thruput_trace::ffi_enums::InvokeErrorCode;
14use thruput_xdr::address_auth::AddressAuthData;
15use thruput_xdr::contract::ContractEntrypointID;
16use thruput_xdr::contract::*;
17use thruput_xdr::network_config::NetworkConfig;
18use thruput_xdr::storage_item::WireStorageItem;
19use thruput_xdr::transaction::*;
20use thruput_xdr::types::*;
21use xdrs::txs;
22
23const TEST_CONTRACT_SIZE: u32 = 1024;
28
29pub const TEST_NETWORK_NAME: &str = "LOCAL_UNIT_TEST";
35
36pub fn test_network_hash() -> NetworkHash {
42 let mut hasher = Sha256::new();
43 hasher.update(TEST_NETWORK_NAME.as_bytes());
44 let hash: [u8; 32] = hasher.finalize().into();
45 NetworkHash::new(hash.into())
46}
47
48pub(crate) struct TestEnvironmentInner {
51 pub(crate) db: DBWrapper,
52 pub(crate) contract_context: RuntimeContextWrapper,
53 mock_contract_index: i32,
54 contracts: Vec<*mut MockContractInner>, }
56
57pub struct TestEnvironment {
92 pub(crate) inner: RefCell<TestEnvironmentInner>,
93}
94
95unsafe extern "C" fn trampoline(
96 context: *mut c_void,
97 method: u64,
98) -> thruput_sys::ForeignInvokeResult {
99 let c: *mut MockContractInner = unsafe { core::mem::transmute(context) };
100
101 let method: ContractEntrypointID = method.to_be_bytes().into();
102
103 match unsafe { (*c).get_func(method).or_else(|| (*c).get_unknown_func()) }
108 {
109 Some(x) => {
110 let _in_body = crate::invoke_error::PanicHookGuard::enter();
115 x()
116 }
117 None => thruput_sys::ForeignInvokeResult {
118 panic: InvokeErrorCode::Deterministic.0,
119 },
120 }
121}
122
123pub(crate) struct DBWrapper {
124 pub(crate) db: *mut c_void,
125}
126
127impl DBWrapper {
128 pub fn new(
129 cost_model: Option<&StorageCostModel>,
130 network_hash: &NetworkHash,
131 genesis_root_contract: &Contract,
132 network_config: Option<&NetworkConfig>,
133 ) -> Self {
134 let cost_model_bytes =
135 cost_model.map(|model| model.serialize().unwrap());
136
137 let ffi_cost_model_bytes: (*const u8, u32) = cost_model_bytes
138 .as_ref()
139 .map(|vec| (vec.as_ptr(), vec.len().try_into().unwrap()))
140 .unwrap_or((core::ptr::null(), 0));
141
142 let genesis_root_bytes = txs::encode(genesis_root_contract).unwrap();
143
144 let network_config_bytes =
145 network_config.map(|config| txs::encode(config).unwrap());
146 let ffi_network_config_bytes: (*const u8, u32) = network_config_bytes
147 .as_ref()
148 .map(|vec| (vec.as_ptr(), vec.len().try_into().unwrap()))
149 .unwrap_or((core::ptr::null(), 0));
150
151 let res = unsafe {
159 thruput_sys::create_db(
160 ffi_cost_model_bytes.0,
161 ffi_cost_model_bytes.1,
162 network_hash.as_ptr(),
163 genesis_root_bytes.as_ptr(),
164 genesis_root_bytes.len().try_into().unwrap(),
165 ffi_network_config_bytes.0,
166 ffi_network_config_bytes.1,
167 )
168 };
169 let err: FFIErrorCode = res.ffi_error_code.into();
170 if err != FFIErrorCode::None {
171 panic!("failed to create db: {}", res.ffi_error_code);
172 }
173 assert!(!res.db.is_null());
174 Self { db: res.db }
175 }
176
177 pub fn get_cost_model(&self) -> StorageCostModel {
178 let res = unsafe { thruput_sys::get_cost_model(self.db) };
179 let err: FFIErrorCode = res.ffi_error_code.into();
180 if err != FFIErrorCode::None {
181 panic!("failed to get cost model: {}", res.ffi_error_code);
182 }
183 if res.data.len == 0 {
184 panic!("failed to get cost model: no data");
185 }
186
187 let data = xdrs::txs::decode::<
188 thruput_xdr::storage_cost::StorageCostModelXdr,
189 >(res.data.as_slice().unwrap())
190 .unwrap();
191 StorageCostModel::new(data)
192 }
193
194 pub fn get_network_config(&self) -> NetworkConfig {
195 let res = unsafe { thruput_sys::get_network_config(self.db) };
196 let err: FFIErrorCode = res.ffi_error_code.into();
197 if err != FFIErrorCode::None {
198 panic!("failed to get network config: {}", res.ffi_error_code);
199 }
200 if res.data.len == 0 {
201 panic!("failed to get network config: no data");
202 }
203
204 xdrs::txs::decode::<NetworkConfig>(res.data.as_slice().unwrap())
205 .unwrap()
206 }
207
208 pub fn get_snapshot_storage_item(
209 &self,
210 addr: &ContractAddress,
211 key: &StorageKeyContractData,
212 ) -> Option<WireStorageItem> {
213 self.get_snapshot_item(
214 addr,
215 StorageKey::ContractData {
216 contract_data_key: *key,
217 },
218 )
219 }
220
221 pub fn get_snapshot_auth_data(
222 &self,
223 addr: &ContractAddress,
224 ) -> Option<AddressAuthData> {
225 match self.get_snapshot_item(addr, StorageKey::AuthData)? {
226 WireStorageItem::kItemTypeAuthData { auth_data } => {
227 Some(auth_data)
228 }
229 other => panic!("auth data key holds a non-auth item: {other:?}"),
230 }
231 }
232
233 fn get_snapshot_item(
234 &self,
235 addr: &ContractAddress,
236 key: StorageKey,
237 ) -> Option<WireStorageItem> {
238 let addrkey = AddressAndKey {
239 address: *addr,
240 key,
241 };
242 let serialized_addrkey = txs::encode(&addrkey).unwrap();
243 let ffi_addrkey = thruput_sys::FFISerializedAddressAndKey {
244 data: serialized_addrkey.as_ptr(),
245 len: serialized_addrkey.len().try_into().unwrap(),
246 };
247
248 let res = unsafe {
249 thruput_sys::get_snapshot_storage_item(self.db, ffi_addrkey)
250 };
251 let err: FFIErrorCode = res.ffi_error_code.into();
252 if err != FFIErrorCode::None {
253 panic!("failed to get snapshot storage item: {err}");
254 }
255
256 res.data
257 .as_slice()
258 .map(|bytes| txs::decode::<WireStorageItem>(bytes).unwrap())
259 }
260}
261
262impl Drop for DBWrapper {
263 fn drop(&mut self) {
264 let r = unsafe { thruput_sys::free_db(self.db) };
265 let err: FFIErrorCode = r.into();
266 if err != FFIErrorCode::None {
267 crate::drop_failure(format_args!("failed to free db: {:?}", err));
268 }
269 }
270}
271
272pub(crate) struct RuntimeContextWrapper {
273 pub context: *mut c_void,
274}
275
276impl RuntimeContextWrapper {
277 pub fn new() -> Self {
278 let res = unsafe { thruput_sys::open_runtime_context() };
279 let err: FFIErrorCode = res.ffi_error_code.into();
280 if err != FFIErrorCode::None {
281 panic!("failed to create runtime context: {}", res.ffi_error_code);
282 }
283 assert!(!res.context.is_null());
284 Self {
285 context: res.context,
286 }
287 }
288}
289
290impl Drop for RuntimeContextWrapper {
291 fn drop(&mut self) {
292 let r = unsafe { thruput_sys::close_runtime_context(self.context) };
293 let err: FFIErrorCode = r.into();
294 if err != FFIErrorCode::None {
295 crate::drop_failure(format_args!(
296 "failed to close runtime context: {:?}",
297 err
298 ));
299 }
300 }
301}
302
303impl TestEnvironmentInner {
304 pub fn new(
309 cost_model: Option<&StorageCostModel>,
310 network_hash: &NetworkHash,
311 genesis_root_contract: &Contract,
312 network_config: Option<&NetworkConfig>,
313 ) -> Self {
314 Self {
317 db: DBWrapper::new(
318 cost_model,
319 network_hash,
320 genesis_root_contract,
321 network_config,
322 ),
323 contract_context: RuntimeContextWrapper::new(),
324 mock_contract_index: 0,
325 contracts: vec![],
326 }
327 }
328
329 pub fn enable_syscall_logging(level: thruput_sys::FFILogLevel) {
332 unsafe { thruput_sys::enable_syscall_logging(level as i32) };
333 }
334
335 pub fn setup_create_contract(
350 &mut self,
351 contract: &Contract,
352 ) -> SHA256Hash {
353 let contract_bytes = txs::encode(&contract).unwrap();
354 let res = unsafe {
355 thruput_sys::setup_create_contract(
356 self.contract_context.context,
357 self.db.db,
358 contract_bytes.as_ptr(),
359 contract_bytes.len().try_into().unwrap(),
360 )
361 };
362 if res != 0 {
363 let err: thruput_sys::FFIErrorCode = res.into();
364 if err == thruput_sys::FFIErrorCode::ContractVerificationFailed {
365 panic!(
366 "setup_create_contract failed: {err}\n\
367 \n\
368 The contract's rv32 artifact failed validation. Common causes:\n\
369 - The raw ELF was deployed instead of the lowered .rv32a image\n\
370 - Memory image bounds too large\n\
371 - Unsupported instructions (only the RV32IM subset is accepted)\n"
372 );
373 }
374 panic!("setup_create_contract failed: {err}");
375 }
376 let mut hasher = Sha256::new();
377 hasher.update(&contract_bytes);
378 let res: [u8; 32] = hasher.finalize().into();
379 res.into()
380 }
381
382 pub fn setup_create_deployment(
396 &mut self,
397 addr: &ContractAddress,
398 contract: &Contract,
399 ) {
400 let contract_bytes = txs::encode(&contract).unwrap();
401
402 let mut hasher = Sha256::new();
403 hasher.update(&contract_bytes);
404 let res: [u8; 32] = hasher.finalize().into();
405 let contract_hash: SHA256Hash = res.into();
406
407 let contract_size = deployment_contract_size(contract);
408
409 let res = unsafe {
410 thruput_sys::setup_deploy_contract(
411 self.db.db,
412 addr.as_ptr(),
413 contract_hash.as_ptr(),
414 contract_size,
415 )
416 };
417 let err: FFIErrorCode = res.into();
418 if err != FFIErrorCode::None {
419 panic!("failed to setup new contract deployment: {:?}", err);
420 }
421 }
422
423 pub fn setup_grant_storage_tokens(
432 &mut self,
433 addr: &ContractAddress,
434 tokens: i64,
435 ) {
436 let res = unsafe {
437 thruput_sys::setup_grant_contract_storage_tokens(
438 self.db.db,
439 addr.as_ptr(),
440 tokens,
441 )
442 };
443 let err: FFIErrorCode = res.into();
444 if err != FFIErrorCode::None {
445 panic!("failed to setup grant storage tokens: {:?}", err);
446 }
447 }
448
449 pub fn advance_block(&mut self) {
457 self.advance_block_inner(std::ptr::null())
458 }
459
460 pub fn advance_block_at(&mut self, block_timepoint: TimePoint) {
466 let raw: u64 = block_timepoint.into_inner();
467 self.advance_block_inner(&raw)
468 }
469
470 fn advance_block_inner(&mut self, block_timepoint: *const u64) {
471 let res = unsafe {
472 thruput_sys::advance_new_block(self.db.db, block_timepoint)
473 };
474 let err: FFIErrorCode = res.into();
475 if err == FFIErrorCode::InvalidArgument {
476 panic!(
477 "failed to advance block: the requested timepoint precedes the \
478 block it would follow. Blocks only move forward in time; pin a \
479 timepoint at or after the current block's."
480 );
481 }
482 if err != FFIErrorCode::None {
483 panic!("failed to advance block: {:?}", err);
484 }
485 }
486
487 pub fn add_contract(&mut self) -> *mut MockContractInner {
496 let out: *mut MockContractInner = Box::into_raw(Box::new(
497 MockContractInner::new(self.mock_contract_index),
498 ));
499
500 let new_id = self.mock_contract_index;
501
502 self.contracts.push(out);
503 self.mock_contract_index += 1;
504
505 let res = unsafe {
506 thruput_sys::register_new_contract(
507 self.contract_context.context,
508 new_id,
509 Some(trampoline),
510 out as *mut c_void,
511 )
512 };
513 let err: FFIErrorCode = res.into();
514 if err != FFIErrorCode::None {
515 panic!("failed to link fn: {:?}", err);
516 }
517
518 out
519 }
520
521 pub fn get_current_cost_model(&self) -> StorageCostModel {
523 self.db.get_cost_model()
524 }
525
526 pub fn get_network_config(&self) -> NetworkConfig {
528 self.db.get_network_config()
529 }
530
531 pub fn run_auth_checks(
534 &self,
535 tx: &SignedTransaction,
536 ) -> thruput_xdr::auth_claim::AuthVerifyErrorCode {
537 let serialized_tx = txs::encode(tx).unwrap();
538 let ffi_serialized_tx = thruput_sys::FFISerializedSignedTransaction {
539 data: serialized_tx.as_ptr() as *mut u8,
540 len: serialized_tx.len().try_into().unwrap(),
541 };
542 let res = unsafe {
543 thruput_sys::verify_signed_transaction_auth_from_db(
544 self.db.db,
545 ffi_serialized_tx,
546 )
547 };
548 let err: FFIErrorCode = res.ffi_error_code.into();
549 if err != FFIErrorCode::None {
550 panic!("failed to run auth checks: {:?}", err);
551 }
552 res.auth_error_code.into()
553 }
554}
555
556impl Drop for TestEnvironmentInner {
557 fn drop(&mut self) {
558 for contract in &self.contracts {
559 let _ = unsafe { Box::from_raw(*contract) };
560 }
561 }
562}
563
564impl Default for TestEnvironmentInner {
565 fn default() -> Self {
566 Self::new(
567 None,
568 &test_network_hash(),
569 &mock_genesis_root_contract(),
570 None,
571 )
572 }
573}
574
575pub type TestEnvironmentHandle = Rc<TestEnvironment>;
577
578pub fn mock_genesis_root_contract() -> Contract {
586 Contract::kMockTest {
587 mock_contract_id: u32::MAX as i32,
588 }
589}
590
591impl TestEnvironment {
592 pub fn new(
595 cost_model: Option<&StorageCostModel>,
596 ) -> TestEnvironmentHandle {
597 Self::with_network_hash(cost_model, &test_network_hash())
598 }
599
600 pub fn with_network_hash(
603 cost_model: Option<&StorageCostModel>,
604 network_hash: &NetworkHash,
605 ) -> TestEnvironmentHandle {
606 Self::with_genesis_root_contract(
607 cost_model,
608 network_hash,
609 &mock_genesis_root_contract(),
610 )
611 }
612
613 pub fn with_genesis_root_contract(
616 cost_model: Option<&StorageCostModel>,
617 network_hash: &NetworkHash,
618 genesis_root_contract: &Contract,
619 ) -> TestEnvironmentHandle {
620 Self::from_parts(cost_model, network_hash, genesis_root_contract, None)
621 }
622
623 pub fn builder() -> TestEnvironmentBuilder {
639 TestEnvironmentBuilder::new()
640 }
641
642 pub(crate) fn from_parts(
646 cost_model: Option<&StorageCostModel>,
647 network_hash: &NetworkHash,
648 genesis_root_contract: &Contract,
649 network_config: Option<&NetworkConfig>,
650 ) -> TestEnvironmentHandle {
651 let te = Rc::new(Self {
652 inner: RefCell::new(TestEnvironmentInner::new(
653 cost_model,
654 network_hash,
655 genesis_root_contract,
656 network_config,
657 )),
658 });
659
660 if std::env::var("SDK_TEST_ENABLE_SYSCALL_LOGGING").is_ok() {
661 Self::enable_syscall_logging();
662 }
663
664 te
665 }
666
667 #[allow(clippy::should_implement_trait)]
670 pub fn default() -> TestEnvironmentHandle {
671 Self::new(None)
672 }
673
674 pub fn no_cost() -> TestEnvironmentHandle {
676 Self::new(Some(
677 &thruput_storage_cost::StorageCostModel::no_cost_model(),
678 ))
679 }
680
681 pub fn enable_syscall_logging() {
684 TestEnvironmentInner::enable_syscall_logging(
685 thruput_sys::FFILogLevel::Trace,
686 );
687 }
688
689 pub fn advance_block(self: &TestEnvironmentHandle) {
703 self.inner.borrow_mut().advance_block()
704 }
705
706 pub fn advance_block_at(
715 self: &TestEnvironmentHandle,
716 block_timepoint: TimePoint,
717 ) {
718 self.inner.borrow_mut().advance_block_at(block_timepoint)
719 }
720
721 pub fn get_current_cost_model(
723 self: &TestEnvironmentHandle,
724 ) -> StorageCostModel {
725 self.inner.borrow().get_current_cost_model()
726 }
727
728 pub fn get_network_config(self: &TestEnvironmentHandle) -> NetworkConfig {
731 self.inner.borrow().get_network_config()
732 }
733
734 pub fn get_snapshot_storage_item(
737 self: &TestEnvironmentHandle,
738 addr: &ContractAddress,
739 key: &StorageKeyContractData,
740 ) -> Option<WireStorageItem> {
741 self.inner.borrow().db.get_snapshot_storage_item(addr, key)
742 }
743
744 pub fn get_snapshot_auth_data(
748 self: &TestEnvironmentHandle,
749 addr: &ContractAddress,
750 ) -> Option<AddressAuthData> {
751 self.inner.borrow().db.get_snapshot_auth_data(addr)
752 }
753
754 #[doc(hidden)]
757 pub fn add_contract(self: &TestEnvironmentHandle) -> MockContract {
758 let inner = self.inner.borrow_mut().add_contract();
759 MockContract::new(inner, self.clone())
760 }
761
762 pub fn register_mock<F>(self: &TestEnvironmentHandle, f: F) -> MockContract
768 where
769 F: FnOnce(&mut MockContract),
770 {
771 let mut c = self.add_contract();
772 f(&mut c);
773 self.setup_create_contract(&c.as_contract());
774 c
775 }
776
777 pub fn transaction(self: &TestEnvironmentHandle) -> TransactionBuilder {
792 TransactionBuilder::new(self.clone())
793 }
794
795 #[must_use]
807 pub fn transact(
808 self: &TestEnvironmentHandle,
809 f: impl FnOnce(&mut MockTransactionHandler) -> FrameResult,
810 ) -> TransactResult {
811 self.transaction().transact(f)
812 }
813
814 pub fn transact_manual<R>(
824 self: &TestEnvironmentHandle,
825 f: impl FnOnce(&mut MockTransactionHandler) -> R,
826 ) -> R {
827 self.transaction().transact_manual(f)
828 }
829
830 pub fn setup_create_contract(
840 self: &TestEnvironmentHandle,
841 contract: &Contract,
842 ) -> SHA256Hash {
843 self.inner.borrow_mut().setup_create_contract(contract)
844 }
845
846 pub fn setup_create_deployment(
849 self: &TestEnvironmentHandle,
850 addr: &ContractAddress,
851 contract: &Contract,
852 ) {
853 self.inner
854 .borrow_mut()
855 .setup_create_deployment(addr, contract)
856 }
857
858 pub fn setup_grant_storage_tokens(
862 self: &TestEnvironmentHandle,
863 addr: &ContractAddress,
864 tokens: i64,
865 ) {
866 self.inner
867 .borrow_mut()
868 .setup_grant_storage_tokens(addr, tokens)
869 }
870
871 pub fn verify_signed_transaction_auth(
878 self: &TestEnvironmentHandle,
879 tx: &SignedTransaction,
880 ) -> thruput_xdr::auth_claim::AuthVerifyErrorCode {
881 self.inner.borrow().run_auth_checks(tx)
882 }
883
884 pub fn verify_signed_transaction_auth_static(
897 tx: &SignedTransaction,
898 source_auth_data: Option<&AddressAuthData>,
899 network_hash: &NetworkHash,
900 ) -> thruput_xdr::auth_claim::AuthVerifyErrorCode {
901 let serialized_tx = txs::encode(tx).unwrap();
902 let ffi_serialized_tx = thruput_sys::FFISerializedSignedTransaction {
903 data: serialized_tx.as_ptr() as *mut u8,
904 len: serialized_tx.len().try_into().unwrap(),
905 };
906
907 let serialized_source_auth_data =
910 source_auth_data.map(|d| txs::encode(d).unwrap());
911 let ffi_serialized_source_auth_data =
912 match &serialized_source_auth_data {
913 Some(bytes) => thruput_sys::FFISourceAuthData {
914 data: bytes.as_ptr() as *mut u8,
915 len: bytes.len().try_into().unwrap(),
916 },
917 None => thruput_sys::FFISourceAuthData {
918 data: core::ptr::null_mut(),
919 len: 0,
920 },
921 };
922
923 let res = unsafe {
924 thruput_sys::verify_signed_transaction_auth(
925 ffi_serialized_tx,
926 ffi_serialized_source_auth_data,
927 network_hash.as_ptr(),
928 )
929 };
930 let err: FFIErrorCode = res.ffi_error_code.into();
931 if err != FFIErrorCode::None {
932 panic!("failed to run auth checks: {:?}", err);
933 }
934 res.auth_error_code.into()
935 }
936}
937
938fn deployment_contract_size(contract: &Contract) -> u32 {
939 match contract {
940 Contract::kNative { .. } => 0,
941 Contract::kMockTest { .. } => TEST_CONTRACT_SIZE,
942 Contract::kRv32 { rv32_contract } => rv32_contract.data.len() as u32,
943 other => panic!("unsupported contract variant: {:?}", other),
944 }
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950 use xdrs::BytesM;
951
952 const TEST_ADDR: ContractAddress = ContractAddress::new([0x11; 32]);
953
954 #[test]
955 fn test_network_hash_is_sha256_of_the_test_network_name() {
956 let expected: [u8; 32] = [
959 0xb0, 0xb2, 0xfc, 0xc8, 0x57, 0xda, 0xa6, 0x04, 0x78, 0x7b, 0xb8,
960 0x4b, 0x45, 0xa4, 0xca, 0xf0, 0x32, 0xea, 0x6b, 0x93, 0xf5, 0xa5,
961 0xed, 0x1c, 0x14, 0xa9, 0xbf, 0xf8, 0xfa, 0x65, 0xbb, 0x52,
962 ];
963 assert_eq!(test_network_hash(), NetworkHash::new(expected.into()));
964 }
965
966 #[test]
967 fn deployment_contract_size_uses_rv32_blob_length() {
968 let contract = Contract::kRv32 {
969 rv32_contract: ContractBlob {
970 data: BytesM(vec![1, 2, 3, 4, 5]),
971 },
972 };
973
974 assert_eq!(deployment_contract_size(&contract), 5);
975 }
976
977 #[test]
978 fn get_snapshot_storage_item_reads_committed_bytestring() {
979 let env = TestEnvironment::new(None);
980 let key = StorageKeyContractData::new([0x22; 32]);
981 env.setup_grant_storage_tokens(&TEST_ADDR, 1_000_000_000);
982
983 let result = env.transact(|tx| {
984 tx.call(&TEST_ADDR).run(|| {
985 thruput_xdr::syscalls::raw_BytestringSet(&key, b"hello")
986 .unwrap();
987 })
988 });
989 result.assert_success();
990
991 env.advance_block();
992
993 let item = env.get_snapshot_storage_item(&TEST_ADDR, &key);
994 assert_eq!(
995 item,
996 Some(WireStorageItem::kItemTypeBytestring {
997 bytestring: b"hello".to_vec().try_into().unwrap(),
998 }),
999 );
1000 }
1001
1002 #[test]
1003 fn get_snapshot_storage_item_returns_none_for_missing_key() {
1004 let env = TestEnvironment::new(None);
1005 let key = StorageKeyContractData::new([0x33; 32]);
1006
1007 assert_eq!(env.get_snapshot_storage_item(&TEST_ADDR, &key), None);
1008 }
1009
1010 #[test]
1011 fn get_snapshot_storage_item_reads_committed_bytestring_on_deployed_address()
1012 {
1013 let env = TestEnvironment::new(None);
1014 let key = StorageKeyContractData::new([0x44; 32]);
1015 env.setup_grant_storage_tokens(&TEST_ADDR, 1_000_000_000);
1016 let _mock = env.inner.borrow_mut().add_contract();
1021 let contract = thruput_xdr::contract::Contract::kMockTest {
1022 mock_contract_id: 0,
1023 };
1024 env.inner.borrow_mut().setup_create_contract(&contract);
1025 env.inner
1026 .borrow_mut()
1027 .setup_create_deployment(&TEST_ADDR, &contract);
1028
1029 let result = env.transact(|tx| {
1030 tx.call(&TEST_ADDR).run(|| {
1031 thruput_xdr::syscalls::raw_BytestringSet(&key, b"hello")
1032 .unwrap();
1033 })
1034 });
1035 result.assert_success();
1036
1037 env.advance_block();
1038
1039 let item = env.get_snapshot_storage_item(&TEST_ADDR, &key);
1040 assert_eq!(
1041 item,
1042 Some(WireStorageItem::kItemTypeBytestring {
1043 bytestring: b"hello".to_vec().try_into().unwrap(),
1044 }),
1045 );
1046 }
1047}