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