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