soroban_env_host_zephyr/
testutils.rs

1use crate::e2e_invoke::ledger_entry_to_ledger_key;
2use crate::storage::EntryWithLiveUntil;
3use crate::{
4    budget::Budget,
5    builtin_contracts::testutils::create_account,
6    storage::{SnapshotSource, Storage},
7    xdr::{
8        AccountId, ContractCostType, LedgerEntry, LedgerKey, PublicKey, ScAddress, ScVal, ScVec,
9        Uint256,
10    },
11    AddressObject, BytesObject, Env, EnvBase, Host, HostError, LedgerInfo, MeteredOrdMap,
12    StorageType, SymbolSmall, Val, VecObject,
13};
14use ed25519_dalek::SigningKey;
15use rand::RngCore;
16use std::panic::{catch_unwind, set_hook, take_hook, UnwindSafe};
17use std::{cell::Cell, collections::BTreeMap, rc::Rc, sync::Once};
18
19/// Catch panics while suppressing the default panic hook that prints to the
20/// console.
21///
22/// For the purposes of test reporting we don't want every panicking (but
23/// caught) contract call to print to the console. This requires overriding
24/// the panic hook, a global resource. This is an awkward thing to do with
25/// tests running in parallel.
26///
27/// This function lazily performs a one-time wrapping of the existing panic
28/// hook. It then uses a thread local variable to track contract call depth.
29/// If a panick occurs during a contract call the original hook is not
30/// called, otherwise it is called.
31pub fn call_with_suppressed_panic_hook<C, R>(closure: C) -> std::thread::Result<R>
32where
33    C: FnOnce() -> R + UnwindSafe,
34{
35    thread_local! {
36        static TEST_CONTRACT_CALL_COUNT: Cell<u64> = const { Cell::new(0) };
37    }
38
39    static WRAP_PANIC_HOOK: Once = Once::new();
40
41    WRAP_PANIC_HOOK.call_once(|| {
42        let existing_panic_hook = take_hook();
43        set_hook(Box::new(move |info| {
44            let calling_test_contract = TEST_CONTRACT_CALL_COUNT.with(|c| c.get() != 0);
45            if !calling_test_contract {
46                existing_panic_hook(info)
47            }
48        }))
49    });
50
51    TEST_CONTRACT_CALL_COUNT.with(|c| {
52        let old_count = c.get();
53        let new_count = old_count.checked_add(1).expect("overflow");
54        c.set(new_count);
55    });
56
57    let res = catch_unwind(closure);
58
59    TEST_CONTRACT_CALL_COUNT.with(|c| {
60        let old_count = c.get();
61        let new_count = old_count.checked_sub(1).expect("overflow");
62        c.set(new_count);
63    });
64
65    res
66}
67
68// Test utilities for the host, used in various tests in sub-modules.
69pub trait AsScVal {
70    fn as_scval(&self) -> ScVal;
71}
72
73impl AsScVal for u32 {
74    fn as_scval(&self) -> ScVal {
75        ScVal::U32(*self)
76    }
77}
78
79impl AsScVal for i32 {
80    fn as_scval(&self) -> ScVal {
81        ScVal::I32(*self)
82    }
83}
84
85impl AsScVal for u64 {
86    fn as_scval(&self) -> ScVal {
87        ScVal::U64(*self)
88    }
89}
90
91impl AsScVal for i64 {
92    fn as_scval(&self) -> ScVal {
93        ScVal::I64(*self)
94    }
95}
96
97impl AsScVal for ScVec {
98    fn as_scval(&self) -> ScVal {
99        ScVal::Vec(Some(self.clone()))
100    }
101}
102
103pub fn generate_account_id(host: &Host) -> AccountId {
104    AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(
105        generate_bytes_array(host),
106    )))
107}
108
109pub fn generate_bytes_array(host: &Host) -> [u8; 32] {
110    let mut bytes: [u8; 32] = Default::default();
111    host.with_test_prng(|chacha| {
112        chacha.fill_bytes(&mut bytes);
113        Ok(())
114    })
115    .unwrap();
116    bytes
117}
118
119pub struct MockSnapshotSource(BTreeMap<Rc<LedgerKey>, (Rc<LedgerEntry>, Option<u32>)>);
120
121impl MockSnapshotSource {
122    pub fn new() -> Self {
123        Self(BTreeMap::<Rc<LedgerKey>, (Rc<LedgerEntry>, Option<u32>)>::new())
124    }
125
126    pub fn from_entries(entries: Vec<(LedgerEntry, Option<u32>)>) -> Self {
127        let mut map = BTreeMap::<Rc<LedgerKey>, (Rc<LedgerEntry>, Option<u32>)>::new();
128        let dummy_budget = Budget::default();
129        for (e, maybe_ttl) in entries {
130            let key = Rc::new(ledger_entry_to_ledger_key(&e, &dummy_budget).unwrap());
131            map.insert(key, (Rc::new(e), maybe_ttl));
132        }
133        Self(map)
134    }
135}
136
137impl SnapshotSource for MockSnapshotSource {
138    fn get(&self, key: &Rc<LedgerKey>) -> Result<Option<EntryWithLiveUntil>, HostError> {
139        if let Some((entry, live_until)) = self.0.get(key) {
140            Ok(Some((Rc::clone(entry), *live_until)))
141        } else {
142            Ok(None)
143        }
144    }
145}
146
147#[cfg(test)]
148pub(crate) fn interface_meta_with_custom_versions(proto: u32, pre: u32) -> Vec<u8> {
149    use crate::xdr::{Limited, Limits, ScEnvMetaEntry, WriteXdr};
150    let iv = crate::meta::make_interface_version(proto, pre);
151    let entry = ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(iv);
152    let bytes = Vec::<u8>::new();
153    let mut w = Limited::new(bytes, Limits::none());
154    entry.write_xdr(&mut w).unwrap();
155    w.inner
156}
157
158impl Host {
159    pub const TEST_PRNG_SEED: &'static [u8; 32] = b"12345678901234567890123456789012";
160
161    pub fn set_test_prng(&self) {
162        self.set_base_prng_seed(*Self::TEST_PRNG_SEED).unwrap();
163    }
164
165    pub fn current_test_protocol() -> u32 {
166        use crate::meta::{get_ledger_protocol_version, INTERFACE_VERSION};
167        if let Ok(vers) = std::env::var("TEST_PROTOCOL") {
168            vers.parse().unwrap()
169        } else {
170            get_ledger_protocol_version(INTERFACE_VERSION)
171        }
172    }
173
174    pub fn set_test_ledger_info_with_current_test_protocol(&self) {
175        self.set_ledger_info(LedgerInfo {
176            protocol_version: Self::current_test_protocol(),
177            sequence_number: 0,
178            timestamp: 0,
179            network_id: [0; 32],
180            base_reserve: 0,
181            min_persistent_entry_ttl: 4096,
182            min_temp_entry_ttl: 16,
183            max_entry_ttl: 6_312_000,
184        })
185        .unwrap();
186    }
187
188    pub fn test_host() -> Self {
189        let host = Host::default();
190        host.set_test_ledger_info_with_current_test_protocol();
191        host
192    }
193
194    pub fn test_host_with_prng() -> Self {
195        let host = Self::test_host();
196        host.set_test_prng();
197        host
198    }
199
200    pub fn test_host_with_recording_footprint() -> Self {
201        let snapshot_source = Rc::<MockSnapshotSource>::new(MockSnapshotSource::new());
202        let storage = Storage::with_recording_footprint(snapshot_source);
203        let host = Host::with_storage_and_budget(storage, Budget::default());
204        host.set_test_ledger_info_with_current_test_protocol();
205        host.set_test_prng();
206        host
207    }
208
209    pub fn test_budget(self, cpu: u64, mem: u64) -> Self {
210        self.with_budget(|budget| {
211            budget.reset_limits(cpu, mem)?; // something big but finite that we may exceed
212            budget.reset_models()?;
213            Ok(())
214        })
215        .unwrap();
216        self
217    }
218
219    pub fn enable_model(
220        self,
221        ty: ContractCostType,
222        const_cpu: u64,
223        lin_cpu: u64,
224        const_mem: u64,
225        lin_mem: u64,
226    ) -> Self {
227        self.with_budget(|budget| {
228            budget.override_model_with_unscaled_params(ty, const_cpu, lin_cpu, const_mem, lin_mem)
229        })
230        .unwrap();
231        self
232    }
233
234    pub fn test_scvec<T: AsScVal>(&self, vals: &[T]) -> Result<ScVec, HostError> {
235        let v: Vec<ScVal> = vals.iter().map(|x| x.as_scval()).collect();
236        self.map_err(v.try_into())
237    }
238
239    pub fn test_vec_obj<T: AsScVal>(&self, vals: &[T]) -> Result<VecObject, HostError> {
240        let v = self.test_scvec(vals)?;
241        Ok(self.to_host_val(&ScVal::Vec(Some(v)))?.try_into()?)
242    }
243
244    pub fn test_vec_val<T: AsScVal>(&self, vals: &[T]) -> Result<Val, HostError> {
245        let v = self.test_scvec(vals)?;
246        self.to_host_val(&ScVal::Vec(Some(v)))
247    }
248
249    pub fn test_bin_scobj(&self, vals: &[u8]) -> Result<ScVal, HostError> {
250        Ok(ScVal::Bytes(self.map_err(vals.to_vec().try_into())?))
251    }
252
253    pub fn test_bin_obj(&self, vals: &[u8]) -> Result<BytesObject, HostError> {
254        let scval: ScVal = self.test_bin_scobj(vals)?;
255        let val: Val = self.to_host_val(&scval)?;
256        Ok(val.try_into()?)
257    }
258
259    // Registers a contract with provided Wasm code and returns the registered
260    // contract's address.
261    // The contract address deterministically depends on the input account and
262    // salt, so this can be used with enforcing ledger footprint (but the
263    // footprint still has to be specified outside of this).
264    pub fn register_test_contract_wasm_from_source_account(
265        &self,
266        contract_wasm: &[u8],
267        account: AccountId,
268        salt: [u8; 32],
269    ) -> Result<AddressObject, HostError> {
270        let _span = tracy_span!("register_test_contract_wasm_from_source_account");
271        // Use source account-based auth in order to avoid using nonces which
272        // won't work well with enforcing ledger footprint.
273        let prev_source_account = self.source_account_id()?;
274        // Use recording auth to skip specifying the auth payload.
275        let prev_auth_manager = self.snapshot_auth_manager()?;
276        self.switch_to_recording_auth(true)?;
277
278        let wasm_hash = self.upload_wasm(self.bytes_new_from_slice(contract_wasm)?)?;
279        self.set_source_account(account.clone())?;
280        let contract_address = self.create_contract(
281            self.add_host_object(ScAddress::Account(account.clone()))?,
282            wasm_hash,
283            self.bytes_new_from_slice(&salt)?,
284        )?;
285        if let Some(prev_account) = prev_source_account {
286            self.set_source_account(prev_account)?;
287        }
288        self.set_auth_manager(prev_auth_manager)?;
289        Ok(contract_address)
290    }
291
292    // Registers a contract with provided Wasm code and returns the registered
293    // contract's address.
294    // The contract address will be generated randomly, so this won't work with
295    // enforcing ledger footprint.
296    pub fn register_test_contract_wasm(&self, contract_wasm: &[u8]) -> AddressObject {
297        self.register_test_contract_wasm_from_source_account(
298            contract_wasm,
299            generate_account_id(self),
300            generate_bytes_array(self),
301        )
302        .unwrap()
303    }
304
305    pub fn new_recording_fuzz_host(
306        contract_wasms: &[&[u8]],
307        data_keys: &BTreeMap<ScVal, (StorageType, bool)>,
308        n_signers: usize,
309    ) -> (Host, Vec<AddressObject>, Vec<ed25519_dalek::SigningKey>) {
310        use crate::builtin_contracts::testutils::{
311            generate_signing_key, signing_key_to_account_id,
312        };
313
314        let host = Self::test_host_with_recording_footprint();
315        host.switch_to_recording_auth(false).unwrap();
316        host.with_budget(|budget| {
317            budget.reset_unlimited()?;
318            Ok(())
319        })
320        .unwrap();
321        let mut contract_addresses = Vec::new();
322        for contract_wasm in contract_wasms.iter() {
323            contract_addresses.push(host.register_test_contract_wasm(contract_wasm));
324        }
325        let ScAddress::Contract(contract_hash) =
326            host.scaddress_from_address(contract_addresses[0]).unwrap()
327        else {
328            panic!()
329        };
330
331        let test = SymbolSmall::try_from_str("test").unwrap();
332
333        // First step: insert all the data values in question into the storage map.
334        host.with_test_contract_frame(contract_hash.clone(), test.into(), || {
335            for (k, (t, _)) in data_keys.iter() {
336                let v = host.to_host_val(k).unwrap();
337                host.put_contract_data(v, v, *t).unwrap();
338            }
339            Ok(Val::VOID.into())
340        })
341        .unwrap();
342
343        // Second step: generate some accounts to sign things with.
344        let signing_keys = (0..n_signers)
345            .map(|_| generate_signing_key(&host))
346            .collect();
347        for signing_key in &signing_keys {
348            create_account(
349                &host,
350                &signing_key_to_account_id(signing_key),
351                vec![(&signing_key, 1)],
352                100_000_000,
353                1,
354                [1, 0, 0, 0],
355                None,
356                None,
357                0,
358            )
359        }
360
361        (host, contract_addresses, signing_keys)
362    }
363
364    pub fn switch_fuzz_host_to_enforcing(
365        &self,
366        data_keys: &BTreeMap<ScVal, (StorageType, bool)>,
367        signing_keys: &[ed25519_dalek::SigningKey],
368    ) {
369        use crate::builtin_contracts::testutils::TestSigner;
370        use crate::xdr::{
371            HashIdPreimage, HashIdPreimageSorobanAuthorization, SorobanAddressCredentials,
372            SorobanAuthorizationEntry, SorobanCredentials,
373        };
374        self.with_budget(|budget| {
375            budget.reset_unlimited()?;
376            Ok(())
377        })
378        .unwrap();
379
380        // Modify footprint entries to read-only-ness as required, synthesize
381        // empty storage-map entries that were accessed by keys the contract made
382        // up, and switch to enforcing mode.
383        self.with_mut_storage(|storage| {
384            storage.footprint.0 = MeteredOrdMap::from_exact_iter(
385                storage
386                    .footprint
387                    .0
388                    .iter(self.budget_ref())
389                    .unwrap()
390                    .map(|(k, accesstype)| {
391                        let mut accesstype = *accesstype;
392                        if let LedgerKey::ContractData(k) = k.as_ref() {
393                            if let Some((_, ro)) = data_keys.get(&k.key) {
394                                if *ro {
395                                    accesstype = crate::storage::AccessType::ReadOnly;
396                                } else {
397                                    accesstype = crate::storage::AccessType::ReadWrite;
398                                }
399                            }
400                        }
401                        (k.clone(), accesstype)
402                    }),
403                self.budget_ref(),
404            )
405            .unwrap();
406
407            // Synthesize empty entries for anything the contract made up (these
408            // will be in the footprint but not yet in the map, which is an
409            // invariant violation we need to repair here).
410            let mut map = BTreeMap::new();
411            for (k, v) in storage.map.iter(self.budget_ref()).unwrap() {
412                map.insert(k.clone(), v.clone());
413            }
414            for (k, _) in storage.footprint.0.iter(self.budget_ref()).unwrap() {
415                if !map.contains_key(k) {
416                    map.insert(k.clone(), None);
417                }
418            }
419            // Reset any nonces so they can be consumed.
420            for (k, v) in map.iter_mut() {
421                if let LedgerKey::ContractData(k) = k.as_ref() {
422                    if let ScVal::LedgerKeyNonce(_) = &k.key {
423                        *v = None;
424                    }
425                }
426            }
427            storage.map = MeteredOrdMap::from_exact_iter(
428                map.iter().map(|(k, v)| (k.clone(), v.clone())),
429                self.budget_ref(),
430            )
431            .unwrap();
432            storage.mode = crate::storage::FootprintMode::Enforcing;
433            Ok(())
434        })
435        .unwrap();
436
437        // Sign and install auth entries for all the recorded auth payloads.
438        let mut auth_entries = Vec::new();
439        let account_signers = signing_keys
440            .iter()
441            .map(TestSigner::account)
442            .collect::<Vec<_>>();
443        for payload in self.get_recorded_auth_payloads().unwrap().iter() {
444            for signer in account_signers.iter() {
445                let Some(address) = &payload.address else {
446                    continue;
447                };
448                let Some(nonce) = payload.nonce else { continue };
449                if *address == ScAddress::Account(signer.account_id()) {
450                    let address = address.clone();
451                    let signature_expiration_ledger =
452                        u32::from(self.get_ledger_sequence().unwrap()) + 10000;
453                    let network_id = self
454                        .with_ledger_info(|li: &LedgerInfo| Ok(li.network_id))
455                        .unwrap()
456                        .try_into()
457                        .unwrap();
458                    let signature_payload_preimage =
459                        HashIdPreimage::SorobanAuthorization(HashIdPreimageSorobanAuthorization {
460                            network_id,
461                            invocation: payload.invocation.clone(),
462                            nonce,
463                            signature_expiration_ledger,
464                        });
465                    let signature_payload =
466                        self.metered_hash_xdr(&signature_payload_preimage).unwrap();
467                    let signature = signer.sign(&self, &signature_payload);
468                    let credentials = SorobanCredentials::Address(SorobanAddressCredentials {
469                        address: address.clone(),
470                        nonce: nonce,
471                        signature_expiration_ledger,
472                        signature,
473                    });
474                    let entry = SorobanAuthorizationEntry {
475                        credentials,
476                        root_invocation: payload.invocation.clone(),
477                    };
478                    auth_entries.push(entry);
479                }
480            }
481        }
482        self.set_authorization_entries(auth_entries).unwrap();
483    }
484
485    #[cfg(all(test, feature = "testutils"))]
486    pub(crate) fn measured_call(
487        &self,
488        contract: AddressObject,
489        func: crate::Symbol,
490        args: VecObject,
491    ) -> Result<Val, HostError> {
492        use crate::{budget::AsBudget, host::TraceEvent};
493        use soroban_bench_utils::HostTracker;
494        use std::cell::RefCell;
495
496        let _span = tracy_span!("measured_call");
497        let budget = self.as_budget();
498        budget.reset_unlimited()?;
499        let ht = Rc::new(RefCell::new(HostTracker::new()));
500
501        if std::env::var("EXCLUDE_VM_INSTANTIATION").is_ok() {
502            let ht2 = ht.clone();
503            let budget2 = budget.clone();
504            self.set_trace_hook(Some(Rc::new(move |_, evt| {
505                if let TraceEvent::PushCtx(_) = evt {
506                    budget2.reset_unlimited()?;
507                    ht2.borrow_mut().start(None);
508                }
509                Ok(())
510            })))?;
511        } else {
512            ht.borrow_mut().start(None);
513        }
514        let val = self.call(contract, func, args);
515        self.set_trace_hook(None)?;
516
517        let (cpu_actual, mem_actual, time_nsecs) = Rc::into_inner(ht).unwrap().into_inner().stop();
518
519        let cpu_metered = budget
520            .get_cpu_insns_consumed()
521            .expect("unable to retrieve cpu consumed");
522        let mem_metered = budget
523            .get_mem_bytes_consumed()
524            .expect("unable to retrieve mem consumed");
525
526        let cpu_diff = (cpu_metered - cpu_actual) as i64;
527        let cpu_metered_diff_percent = 100 * cpu_diff / (cpu_metered as i64).max(1);
528        let mem_diff = (mem_metered - mem_actual) as i64;
529        let mem_metered_diff_percent = 100 * mem_diff / (mem_metered as i64).max(1);
530        let metered_insn_nsecs_ratio: f64 = (cpu_metered as f64) / (time_nsecs as f64).max(1.0);
531        let actual_insn_nsecs_ratio: f64 = (cpu_actual as f64) / (time_nsecs as f64).max(1.0);
532
533        println!();
534        println!(
535            "metered cpu insns: {}, actual cpu insns {}, diff: {} ({:.3}%)",
536            cpu_metered, cpu_actual, cpu_diff, cpu_metered_diff_percent
537        );
538        println!(
539            "metered mem bytes: {}, actual mem bytes {}, diff: {} ({:.3}%)",
540            mem_metered, mem_actual, mem_diff, mem_metered_diff_percent
541        );
542        println!("time_nsecs: {}", time_nsecs);
543        println!(
544            "metered cpu_insn/time_nsecs ratio: {:.3}",
545            metered_insn_nsecs_ratio
546        );
547        println!(
548            "actual cpu_insn/time_nsecs ratio: {:.3}",
549            actual_insn_nsecs_ratio
550        );
551        println!();
552
553        val
554    }
555}
556
557#[cfg(test)]
558pub(crate) mod wasm {
559    use crate::{Symbol, Tag, U32Val, Val};
560    use soroban_synth_wasm::{Arity, FuncRef, LocalRef, ModEmitter, Operand};
561    use wasm_encoder::{ConstExpr, Elements, RefType};
562
563    pub(crate) fn wasm_module_with_4n_insns(n: usize) -> Vec<u8> {
564        let mut fe = ModEmitter::default_with_test_protocol().func(Arity(1), 0);
565        let arg = fe.args[0];
566        fe.push(Operand::Const64(1));
567        for i in 0..n {
568            fe.push(arg.0);
569            fe.push(Operand::Const64(i as i64));
570            fe.i64_mul();
571            fe.i64_add();
572        }
573        fe.drop();
574        fe.push(Symbol::try_from_small_str("pass").unwrap());
575        fe.finish_and_export("test").finish()
576    }
577
578    pub(crate) fn wasm_module_with_n_funcs_no_export(n: usize) -> Vec<u8> {
579        let mut me = ModEmitter::default_with_test_protocol();
580        for _i in 0..n {
581            let mut fe = me.func(Arity(0), 0);
582            fe.push(Symbol::try_from_small_str("pass").unwrap());
583            me = fe.finish().0;
584        }
585        me.finish()
586    }
587
588    pub(crate) fn wasm_module_with_repeated_exporting_the_same_func(n: usize) -> Vec<u8> {
589        let me = ModEmitter::default_with_test_protocol();
590        let mut fe = me.func(Arity(0), 0);
591        fe.push(Symbol::try_from_small_str("pass").unwrap());
592        let (mut me, fid) = fe.finish();
593        for i in 0..n {
594            me.export_func(fid, format!("test{}", i).as_str());
595        }
596        me.finish_no_validate()
597    }
598
599    pub(crate) fn wasm_module_with_mem_grow(n_pages: usize) -> Vec<u8> {
600        let mut fe = ModEmitter::default_with_test_protocol().func(Arity(0), 0);
601        fe.push(Operand::Const32(n_pages as i32));
602        fe.memory_grow();
603        fe.drop();
604        fe.push(Symbol::try_from_small_str("pass").unwrap());
605        fe.finish_and_export("test").finish()
606    }
607
608    pub(crate) fn wasm_module_with_linear_memory_logging() -> Vec<u8> {
609        let mut me = ModEmitter::default_with_test_protocol();
610        // log_from_linear_memory
611        let f0 = me.import_func("x", "_", Arity(4));
612        // the caller
613        let mut fe = me.func(Arity(4), 0);
614        fe.push(Operand::Local(LocalRef(0)));
615        fe.push(Operand::Local(LocalRef(1)));
616        fe.push(Operand::Local(LocalRef(2)));
617        fe.push(Operand::Local(LocalRef(3)));
618        fe.call_func(f0);
619        fe.drop();
620        fe.push(Symbol::try_from_small_str("pass").unwrap());
621        fe.finish_and_export("test").finish()
622    }
623
624    pub(crate) fn wasm_module_with_unreachable() -> Vec<u8> {
625        let me = ModEmitter::default_with_test_protocol();
626        let mut fe = me.func(Arity(0), 0);
627        fe.trap();
628        fe.finish_and_export("test").finish()
629    }
630
631    pub(crate) fn wasm_module_with_indirect_call() -> Vec<u8> {
632        let mut me = ModEmitter::default_with_test_protocol();
633        // an imported function
634        let f0 = me.import_func("t", "_", Arity(0));
635        // a local wasm function
636        let mut fe = me.func(Arity(0), 0);
637        fe.push(Symbol::try_from_small_str("pass").unwrap());
638        let (me, f1) = fe.finish();
639        // another local wasm function
640        let mut fe = me.func(Arity(0), 0);
641        fe.push(Symbol::try_from_small_str("pass2").unwrap());
642        let (mut me, f2) = fe.finish();
643        // store in table
644        me.define_elem_funcs(&[f0, f1, f2]);
645        let ty = me.get_fn_type(Arity(0), Arity(1));
646        // the caller
647        fe = me.func(Arity(1), 0);
648        fe.local_get(LocalRef(0));
649        fe.i32_wrap_i64();
650        // fe.i32_const(0);
651        fe.call_func_indirect(ty);
652        fe.finish_and_export("test").finish()
653    }
654
655    pub(crate) fn wasm_module_with_div_by_zero() -> Vec<u8> {
656        let me = ModEmitter::default_with_test_protocol();
657        let mut fe = me.func(Arity(0), 0);
658        fe.push(Operand::Const64(123));
659        fe.push(Operand::Const64(0));
660        fe.i64_div_s();
661        fe.finish_and_export("test").finish()
662    }
663
664    pub(crate) fn wasm_module_with_integer_overflow() -> Vec<u8> {
665        let me = ModEmitter::default_with_test_protocol();
666        let mut fe = me.func(Arity(0), 0);
667        fe.push(Operand::Const64(i64::MIN));
668        fe.push(Operand::Const64(-1));
669        // interestingly the only operation that can trigger `IntegerOverflow`
670        // is an overflowing division. Other arithmatic opeartions add, sub, mul
671        // are wrapping.
672        fe.i64_div_s();
673        fe.finish_and_export("test").finish()
674    }
675
676    pub(crate) fn wasm_module_with_user_specified_initial_size(
677        mem_pages: u32,
678        elem_count: u32,
679    ) -> Vec<u8> {
680        let me = ModEmitter::from_configs(mem_pages, elem_count);
681        // a local wasm function
682        let mut fe = me.func(Arity(0), 0);
683        fe.push(Symbol::try_from_small_str("pass").unwrap());
684        fe.finish_and_export("test").finish()
685    }
686
687    pub(crate) fn wasm_module_with_large_data_segment(
688        mem_pages: u32,
689        mem_offset: u32,
690        len: u32,
691    ) -> Vec<u8> {
692        let mut me = ModEmitter::from_configs(mem_pages, 128);
693        me.define_data_segment(mem_offset, vec![0; len as usize]);
694        // a local wasm function
695        let mut fe = me.func(Arity(0), 0);
696        fe.push(Symbol::try_from_small_str("pass").unwrap());
697        fe.finish_and_export("test").finish()
698    }
699
700    pub(crate) fn wasm_module_with_multiple_data_segments(
701        num_pages: u32,
702        num_sgmts: u32,
703        seg_size: u32,
704    ) -> Vec<u8> {
705        let mut me = ModEmitter::from_configs(num_pages, 128);
706        let mem_len = num_pages * 0x10_000;
707        // we just make sure the total memory can fit one segments the segments
708        // will just cycle through the space and possibly override earlier ones
709        let max_segments = (mem_len / seg_size.max(1)).max(1);
710        for _i in 0..num_sgmts % max_segments {
711            me.define_data_segment(0, vec![0; seg_size as usize]);
712        }
713        // a local wasm function
714        let mut fe = me.func(Arity(0), 0);
715        fe.push(Symbol::try_from_small_str("pass").unwrap());
716        fe.finish_and_export("test").finish()
717    }
718
719    pub(crate) fn wasm_module_with_large_bytes_from_linear_memory(
720        num_bytes: u32,
721        initial: u8,
722    ) -> Vec<u8> {
723        let num_pages = num_bytes / 0x10_000 + 1;
724        let mut me = ModEmitter::from_configs(num_pages, 128);
725        me.define_data_segment(0, vec![initial; num_bytes as usize]);
726        let mut fe = me.func(Arity(0), 0);
727        fe.bytes_new_from_linear_memory(U32Val::from(0), U32Val::from(num_bytes));
728        fe.finish_and_export("test").finish()
729    }
730
731    pub(crate) fn wasm_module_with_large_vector_from_linear_memory(
732        num_vals: u32,
733        initial: Val,
734    ) -> Vec<u8> {
735        let num_pages = num_vals * 8 / 0x10_000 + 1;
736        let mut me = ModEmitter::from_configs(num_pages, 128);
737        let bytes: Vec<u8> = (0..num_vals)
738            .into_iter()
739            .map(|_| initial.get_payload().to_le_bytes())
740            .flat_map(|a| a.into_iter())
741            .collect();
742        me.define_data_segment(0, bytes);
743        let mut fe = me.func(Arity(0), 0);
744        fe.vec_new_from_linear_memory(U32Val::from(0), U32Val::from(num_vals));
745        fe.finish_and_export("test").finish()
746    }
747
748    pub(crate) fn wasm_module_with_large_map_from_linear_memory(
749        num_vals: u32,
750        initial: Val,
751    ) -> Vec<u8> {
752        // slice is 8 bytes, we choose 8 byte key, val is 8 bytes
753        let num_pages = (num_vals * 8) * 3 / 0x10_000 + 1;
754        let mut me = ModEmitter::from_configs(num_pages, 128);
755
756        let key_bytes: Vec<u8> = (0..num_vals)
757            .into_iter()
758            .map(|i| format!("{:0>width$}", i, width = 8))
759            .flat_map(|s| s.into_bytes().into_iter())
760            .collect();
761
762        let val_bytes: Vec<u8> = (0..num_vals)
763            .into_iter()
764            .map(|_| initial.get_payload().to_le_bytes())
765            .flat_map(|a| a.into_iter())
766            .collect();
767
768        let slices: Vec<u8> = (0..num_vals)
769            .into_iter()
770            .map(|ptr| {
771                let slice = 8_u64 << 32 | (ptr * 8) as u64;
772                slice.to_le_bytes()
773            })
774            .flat_map(|s| s.into_iter())
775            .collect();
776
777        let bytes: Vec<u8> = key_bytes
778            .into_iter()
779            .chain(val_bytes.into_iter())
780            .chain(slices.into_iter())
781            .collect();
782
783        me.define_data_segment(0, bytes);
784        let mut fe = me.func(Arity(0), 0);
785
786        let vals_pos = U32Val::from(num_vals * 8);
787        let keys_pos = U32Val::from(num_vals * 16);
788        fe.map_new_from_linear_memory(keys_pos, vals_pos, U32Val::from(num_vals));
789
790        fe.finish_and_export("test").finish()
791    }
792
793    pub(crate) fn wasm_module_with_data_count(
794        num_sgmts: u32,
795        seg_size: u32,
796        data_count: u32,
797    ) -> Vec<u8> {
798        // first calculate the number of memory pages needed to fit all the data
799        // segments non-overlapping
800        let pages = num_sgmts * seg_size / 0x10_000 + 1;
801        let mut me = ModEmitter::from_configs(pages, 128);
802        for i in 0..num_sgmts {
803            me.define_data_segment(i * seg_size, vec![7; seg_size as usize]);
804        }
805        // define the data count, if count != num_sgmts, then the validator is
806        // supposed to catch it
807        me.data_count(data_count);
808        me.finish_no_validate()
809    }
810
811    // Emit a wasm module that uses post-MVP WASM features. Specifically
812    // mutable-globals and sign-ext.
813    pub fn post_mvp_wasm_module() -> Vec<u8> {
814        let mut me = ModEmitter::default_with_test_protocol();
815
816        // Emit an exported mutable global
817        me.define_global_i64(-100, true, Some("global"));
818
819        let mut fe = me.func(Arity(0), 0);
820        fe.i64_const(0x0000_0000_ffff_abcd_u64 as i64);
821
822        // Emit an op from the new sign-ext repertoire
823        fe.i64_extend32s();
824
825        // Turn this into a I64Small
826        fe.i64_const(8);
827        fe.i64_shl();
828        fe.i64_const(Tag::I64Small as i64);
829        fe.i64_or();
830
831        fe.finish_and_export("test").finish()
832    }
833
834    pub fn empty_wasm_module() -> Vec<u8> {
835        ModEmitter::new().finish()
836    }
837
838    pub fn wasm_module_with_custom_section(name: &str, data: &[u8]) -> Vec<u8> {
839        let mut me = ModEmitter::new();
840        me.custom_section(name, data);
841        me.finish()
842    }
843
844    pub fn wasm_module_with_floating_point_ops() -> Vec<u8> {
845        let me = ModEmitter::default_with_test_protocol();
846        let mut fe = me.func(Arity(0), 0);
847        fe.f64_const(1.1f64);
848        fe.f64_const(2.2f64);
849        fe.f64_add();
850        fe.drop();
851        fe.push(Symbol::try_from_small_str("pass").unwrap());
852        fe.finish_and_export("test").finish()
853    }
854
855    pub fn wasm_module_with_multiple_memories() -> Vec<u8> {
856        let mut me = ModEmitter::new();
857        me.memory(1, None, false, false);
858        me.memory(1, None, false, false);
859        me.finish()
860    }
861
862    pub fn wasm_module_lying_about_import_function_type() -> Vec<u8> {
863        let mut me = ModEmitter::default_with_test_protocol();
864        // function {"t", "_"} is "dummy0", taking 0 arguments
865        // this will not pass the wasmi linker
866        me.import_func("t", "_", Arity(1));
867        me.finish()
868    }
869
870    pub fn wasm_module_importing_nonexistent_function() -> Vec<u8> {
871        let mut me = ModEmitter::default_with_test_protocol();
872        me.import_func("t", "z", Arity(1));
873        me.finish()
874    }
875
876    pub fn wasm_module_with_duplicate_function_import(n: u32) -> Vec<u8> {
877        let mut me = ModEmitter::default_with_test_protocol();
878        for _ in 0..n {
879            me.import_func_no_check("t", "_", Arity(0));
880        }
881        me.finish()
882    }
883
884    pub fn wasm_module_with_nonexistent_function_export() -> Vec<u8> {
885        let mut me = ModEmitter::default_with_test_protocol();
886        me.import_func("t", "_", Arity(0));
887        let mut fe = me.func(Arity(0), 0);
888        fe.push(Symbol::try_from_small_str("pass").unwrap());
889        let (mut me, fid) = fe.finish();
890        // exporting a function I defined is okay
891        me.export_func(fid, "test");
892        // exporting an imported function is also okay, although weird
893        me.export_func(FuncRef(0), "test0");
894        // importing an non-existent function will not pass validation
895        me.export_func(FuncRef(100), "test100");
896        me.finish_no_validate()
897    }
898
899    pub(crate) fn wasm_module_with_nonexistent_func_element() -> Vec<u8> {
900        let mut me = ModEmitter::default_with_test_protocol();
901        // an imported function
902        let f0 = me.import_func("t", "_", Arity(0));
903        // a local wasm function
904        let mut fe = me.func(Arity(0), 0);
905        fe.push(Symbol::try_from_small_str("pass").unwrap());
906        let (me, f1) = fe.finish();
907        // another local wasm function
908        let mut fe = me.func(Arity(0), 0);
909        fe.push(Symbol::try_from_small_str("pass2").unwrap());
910        let (mut me, f2) = fe.finish();
911        // store in table, FuncRef(100) is invalid
912        me.define_elem_funcs(&[f0, f1, f2, FuncRef(100)]);
913        me.finish_no_validate()
914    }
915
916    pub(crate) fn wasm_module_with_start_function() -> Vec<u8> {
917        let me = ModEmitter::default_with_test_protocol();
918        let fe = me.func_with_arity_and_ret(Arity(0), Arity(0), 0);
919        let (mut me, fid) = fe.finish();
920        me.export_func(fid.clone(), "start");
921        me.start(fid);
922        me.finish_no_validate()
923    }
924
925    pub(crate) fn wasm_module_with_multi_value() -> Vec<u8> {
926        let me = ModEmitter::default_with_test_protocol();
927        let mut fe = me.func_with_arity_and_ret(Arity(0), Arity(2), 0);
928        fe.push(Symbol::try_from_small_str("pass1").unwrap());
929        fe.push(Symbol::try_from_small_str("pass2").unwrap());
930        fe.finish_and_export("test").finish()
931    }
932
933    // if n > m, we have oob elements
934    pub(crate) fn wasm_module_large_elements(m: u32, n: u32) -> Vec<u8> {
935        let mut me = ModEmitter::from_configs(1, m);
936        // an imported function
937        let f0 = me.import_func("t", "_", Arity(0));
938        me.define_elem_funcs(vec![f0; n as usize].as_slice());
939        me.finish()
940    }
941
942    pub(crate) fn wasm_module_various_constexpr_in_elements(case: u32) -> Vec<u8> {
943        let mut me = ModEmitter::default_with_test_protocol();
944        // an imported function
945        let f0 = me.import_func("t", "_", Arity(0));
946
947        match case {
948            0 =>
949            // err: wrong type, expect i32, passing i64
950            {
951                me.define_active_elements(
952                    None,
953                    &ConstExpr::i64_const(0),
954                    Elements::Functions(&[f0.0]),
955                )
956            }
957            // err: fp
958            1 => me.define_active_elements(
959                None,
960                &ConstExpr::f64_const(1.0),
961                Elements::Functions(&[f0.0]),
962            ),
963            // err: simd
964            2 => me.define_active_elements(
965                None,
966                &ConstExpr::v128_const(1),
967                Elements::Functions(&[f0.0]),
968            ),
969            // err: wrong type, expect i32, passing funcref
970            3 => me.define_active_elements(
971                None,
972                &ConstExpr::ref_func(f0.0),
973                Elements::Functions(&[f0.0]),
974            ),
975            _ => panic!("not a valid option"),
976        }
977        me.finish_no_validate()
978    }
979
980    pub(crate) fn wasm_module_large_globals(n: u32) -> Vec<u8> {
981        let mut me = ModEmitter::default_with_test_protocol();
982        for i in 0..n {
983            me.define_global_i64(i as i64, true, None);
984        }
985        me.finish()
986    }
987
988    pub(crate) fn wasm_module_various_constexr_in_global(case: u32) -> Vec<u8> {
989        let mut me = ModEmitter::default_with_test_protocol();
990        match case {
991            0 =>
992            // err: mismatch
993            {
994                me.define_global(wasm_encoder::ValType::I32, true, &ConstExpr::i64_const(1))
995            }
996            1 =>
997            // err: fp
998            {
999                me.define_global(wasm_encoder::ValType::F32, true, &ConstExpr::f32_const(1.0))
1000            }
1001            2 =>
1002            // err: simd
1003            {
1004                me.define_global(wasm_encoder::ValType::V128, true, &ConstExpr::v128_const(1))
1005            }
1006            3 =>
1007            // okay: func
1008            {
1009                let fr = me.import_func("t", "_", Arity(0));
1010                me.define_global(
1011                    wasm_encoder::ValType::Ref(RefType::FUNCREF),
1012                    true,
1013                    &ConstExpr::ref_func(fr.0),
1014                )
1015            }
1016            _ => panic!("not a valid option"),
1017        }
1018        me.finish_no_validate()
1019    }
1020
1021    pub(crate) fn wasm_module_various_constexr_in_data_segment(case: u32) -> Vec<u8> {
1022        let mut me = ModEmitter::default_with_test_protocol();
1023        // an imported function
1024        let f0 = me.import_func("t", "_", Arity(0));
1025
1026        match case {
1027            0 =>
1028            // err: wrong type, expect i32, passing i64
1029            {
1030                me.define_active_data(0, &ConstExpr::i64_const(0), vec![0; 8]);
1031            }
1032            // err: fp
1033            1 => {
1034                me.define_active_data(0, &ConstExpr::f64_const(0.0), vec![0; 8]);
1035            }
1036            // err: simd
1037            2 => {
1038                me.define_active_data(0, &ConstExpr::v128_const(0), vec![0; 8]);
1039            }
1040            // err: wrong type, expect i32, passing funcref
1041            3 => {
1042                me.define_active_data(0, &ConstExpr::ref_func(f0.0), vec![0; 8]);
1043            }
1044            _ => panic!("not a valid option"),
1045        }
1046        me.finish_no_validate()
1047    }
1048
1049    pub(crate) fn wasm_module_with_extern_ref() -> Vec<u8> {
1050        let mut me = ModEmitter::new();
1051        me.table(RefType::EXTERNREF, 2, None);
1052        me.add_test_protocol_version_meta();
1053        me.finish_no_validate()
1054    }
1055
1056    pub(crate) fn wasm_module_with_additional_tables(n: u32) -> Vec<u8> {
1057        let mut me = ModEmitter::default_with_test_protocol();
1058        // by default, module already includes a table, here we are creating
1059        // additional ones
1060        for _i in 0..n {
1061            me.table(RefType::FUNCREF, 2, None);
1062        }
1063        // wasmparser has an limit of 100 tables. wasmi does not have such a limit
1064        me.finish_no_validate()
1065    }
1066
1067    // The only type allowed in the MVP is the function type. There are more
1068    // composite types defined by the GC proposal, which should not be allowed
1069    // and we will verify that in another test.
1070    pub(crate) fn wasm_module_with_many_func_types(n: u32) -> Vec<u8> {
1071        let mut me = ModEmitter::default_with_test_protocol();
1072        for _i in 0..n {
1073            // it is allowed to define the same types over and over
1074            me.add_fn_type_no_check(Arity(0), Arity(0));
1075        }
1076        me.finish()
1077    }
1078
1079    pub(crate) fn wasm_module_with_simd_add_i32x4() -> Vec<u8> {
1080        let me = ModEmitter::default_with_test_protocol();
1081        let mut fe = me.func(Arity(0), 0);
1082        // we load [u32, u32, u32, u32] x 2, add them and store back
1083        fe.i32_const(32); // ptr for storing the result
1084        fe.i32_const(0); // ptr for the first 4xi32
1085        fe.v128_load(0, 0);
1086        fe.i32_const(16); // ptr for the second 4xi32
1087        fe.v128_load(0, 0);
1088        fe.i32x4_add();
1089        fe.v128_store(0, 0);
1090        fe.push(Symbol::try_from_small_str("pass").unwrap());
1091        fe.finish_and_export("test").finish()
1092    }
1093
1094    pub(crate) fn wasm_module_calling_protocol_gated_host_fn(wasm_proto: u32) -> Vec<u8> {
1095        let mut me = ModEmitter::new();
1096        me.add_protocol_version_meta(wasm_proto);
1097        // protocol_gated_dummy
1098        let f0 = me.import_func("t", "0", Arity(0));
1099        // the caller
1100        let mut fe = me.func(Arity(0), 0);
1101        fe.call_func(f0);
1102        fe.finish_and_export("test").finish()
1103    }
1104
1105    pub(crate) fn wasm_module_with_a_bit_of_everything(wasm_proto: u32) -> Vec<u8> {
1106        let mut me = ModEmitter::new();
1107        me.add_protocol_version_meta(wasm_proto);
1108        me.table(RefType::FUNCREF, 128, None);
1109        me.memory(1, None, false, false);
1110        me.global(wasm_encoder::ValType::I64, true, &ConstExpr::i64_const(42));
1111        me.export("memory", wasm_encoder::ExportKind::Memory, 0);
1112        let _f0 = me.import_func("t", "_", Arity(0));
1113        let mut fe = me.func(Arity(0), 0);
1114        fe.push(Operand::Const64(1));
1115        fe.push(Operand::Const64(2));
1116        fe.i64_add();
1117        fe.drop();
1118        fe.push(Symbol::try_from_small_str("pass").unwrap());
1119        let (mut me, fid) = fe.finish();
1120        me.export_func(fid, "test");
1121        me.define_elem_funcs(&[fid]);
1122        me.define_data_segment(0x1234, vec![0; 512]);
1123        me.finish()
1124    }
1125}
1126
1127#[allow(clippy::type_complexity)]
1128pub fn simple_account_sign_fn<'a>(
1129    host: &'a Host,
1130    kp: &'a SigningKey,
1131) -> Box<dyn Fn(&[u8]) -> Val + 'a> {
1132    use crate::builtin_contracts::testutils::sign_payload_for_ed25519;
1133    Box::new(|payload: &[u8]| -> Val { sign_payload_for_ed25519(host, kp, payload).into() })
1134}