1#![cfg(any(test, feature = "testutils"))]
2#![cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
3
4pub mod arbitrary;
7
8mod sign;
9use std::rc::Rc;
10
11pub use sign::ed25519;
12
13mod mock_auth;
14pub use mock_auth::{
15 AuthorizedFunction, AuthorizedInvocation, MockAuth, MockAuthContract, MockAuthInvoke,
16};
17use soroban_env_host::TryIntoVal;
18
19pub mod storage;
20
21pub mod cost_estimate;
22
23use crate::{xdr, ConstructorArgs, Env, Val, Vec};
24use soroban_ledger_snapshot::LedgerSnapshot;
25
26pub use crate::env::EnvTestConfig;
27
28pub trait Register {
29 fn register<'i, I, A>(self, env: &Env, id: I, args: A) -> crate::Address
30 where
31 I: Into<Option<&'i crate::Address>>,
32 A: ConstructorArgs;
33}
34
35impl<C> Register for C
36where
37 C: ContractFunctionSet + 'static,
38{
39 fn register<'i, I, A>(self, env: &Env, id: I, args: A) -> crate::Address
40 where
41 I: Into<Option<&'i crate::Address>>,
42 A: ConstructorArgs,
43 {
44 env.register_contract_with_constructor(id, self, args)
45 }
46}
47
48impl<'w> Register for &'w [u8] {
49 fn register<'i, I, A>(self, env: &Env, id: I, args: A) -> crate::Address
50 where
51 I: Into<Option<&'i crate::Address>>,
52 A: ConstructorArgs,
53 {
54 env.register_contract_wasm_with_constructor(id, self, args)
55 }
56}
57
58#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
59#[serde(rename_all = "snake_case")]
60pub struct Snapshot {
61 pub generators: Generators,
62 pub auth: AuthSnapshot,
63 pub ledger: LedgerSnapshot,
64 pub events: EventsSnapshot,
65}
66
67impl Snapshot {
68 pub fn read(r: impl std::io::Read) -> Result<Snapshot, std::io::Error> {
70 Ok(serde_json::from_reader::<_, Snapshot>(r)?)
71 }
72
73 pub fn read_file(p: impl AsRef<std::path::Path>) -> Result<Snapshot, std::io::Error> {
75 Self::read(std::fs::File::open(p)?)
76 }
77
78 pub fn write(&self, w: impl std::io::Write) -> Result<(), std::io::Error> {
80 Ok(serde_json::to_writer_pretty(w, self)?)
81 }
82
83 pub fn write_file(&self, p: impl AsRef<std::path::Path>) -> Result<(), std::io::Error> {
85 let p = p.as_ref();
86 if let Some(dir) = p.parent() {
87 if !dir.exists() {
88 std::fs::create_dir_all(dir)?;
89 }
90 }
91 self.write(std::fs::File::create(p)?)
92 }
93}
94
95#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
96#[serde(rename_all = "snake_case")]
97pub struct EventsSnapshot(pub std::vec::Vec<EventSnapshot>);
98
99impl EventsSnapshot {
100 pub fn read(r: impl std::io::Read) -> Result<EventsSnapshot, std::io::Error> {
102 Ok(serde_json::from_reader::<_, EventsSnapshot>(r)?)
103 }
104
105 pub fn read_file(p: impl AsRef<std::path::Path>) -> Result<EventsSnapshot, std::io::Error> {
107 Self::read(std::fs::File::open(p)?)
108 }
109
110 pub fn write(&self, w: impl std::io::Write) -> Result<(), std::io::Error> {
112 Ok(serde_json::to_writer_pretty(w, self)?)
113 }
114
115 pub fn write_file(&self, p: impl AsRef<std::path::Path>) -> Result<(), std::io::Error> {
117 let p = p.as_ref();
118 if let Some(dir) = p.parent() {
119 if !dir.exists() {
120 std::fs::create_dir_all(dir)?;
121 }
122 }
123 self.write(std::fs::File::create(p)?)
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
128#[serde(rename_all = "snake_case")]
129pub struct EventSnapshot {
130 pub event: xdr::ContractEvent,
131 pub failed_call: bool,
132}
133
134impl From<crate::env::internal::events::HostEvent> for EventSnapshot {
135 fn from(v: crate::env::internal::events::HostEvent) -> Self {
136 Self {
137 event: v.event,
138 failed_call: v.failed_call,
139 }
140 }
141}
142
143#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
144#[serde(rename_all = "snake_case")]
145pub struct AuthSnapshot(
146 pub std::vec::Vec<std::vec::Vec<(xdr::ScAddress, xdr::SorobanAuthorizedInvocation)>>,
147);
148
149impl AuthSnapshot {
150 pub fn read(r: impl std::io::Read) -> Result<AuthSnapshot, std::io::Error> {
152 Ok(serde_json::from_reader::<_, AuthSnapshot>(r)?)
153 }
154
155 pub fn read_file(p: impl AsRef<std::path::Path>) -> Result<AuthSnapshot, std::io::Error> {
157 Self::read(std::fs::File::open(p)?)
158 }
159
160 pub fn write(&self, w: impl std::io::Write) -> Result<(), std::io::Error> {
162 Ok(serde_json::to_writer_pretty(w, self)?)
163 }
164
165 pub fn write_file(&self, p: impl AsRef<std::path::Path>) -> Result<(), std::io::Error> {
167 let p = p.as_ref();
168 if let Some(dir) = p.parent() {
169 if !dir.exists() {
170 std::fs::create_dir_all(dir)?;
171 }
172 }
173 self.write(std::fs::File::create(p)?)
174 }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
178#[serde(rename_all = "snake_case")]
179pub struct Generators {
180 address: u64,
181 nonce: i64,
182 mux_id: u64,
183}
184
185impl Default for Generators {
186 fn default() -> Generators {
187 Generators {
188 address: 0,
189 nonce: 0,
190 mux_id: 0,
191 }
192 }
193}
194
195impl Generators {
196 pub fn read(r: impl std::io::Read) -> Result<Generators, std::io::Error> {
198 Ok(serde_json::from_reader::<_, Generators>(r)?)
199 }
200
201 pub fn read_file(p: impl AsRef<std::path::Path>) -> Result<Generators, std::io::Error> {
203 Self::read(std::fs::File::open(p)?)
204 }
205
206 pub fn write(&self, w: impl std::io::Write) -> Result<(), std::io::Error> {
208 Ok(serde_json::to_writer_pretty(w, self)?)
209 }
210
211 pub fn write_file(&self, p: impl AsRef<std::path::Path>) -> Result<(), std::io::Error> {
213 let p = p.as_ref();
214 if let Some(dir) = p.parent() {
215 if !dir.exists() {
216 std::fs::create_dir_all(dir)?;
217 }
218 }
219 self.write(std::fs::File::create(p)?)
220 }
221}
222
223impl Generators {
224 pub fn address(&mut self) -> [u8; 32] {
225 self.address = self.address.checked_add(1).unwrap();
226 let b: [u8; 8] = self.address.to_be_bytes();
227 [
228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b[0], b[1],
229 b[2], b[3], b[4], b[5], b[6], b[7],
230 ]
231 }
232
233 pub fn nonce(&mut self) -> i64 {
234 self.nonce = self.nonce.checked_add(1).unwrap();
235 self.nonce
236 }
237
238 pub fn mux_id(&mut self) -> u64 {
239 self.mux_id = self.mux_id.checked_add(1).unwrap();
240 self.mux_id
241 }
242}
243
244#[doc(hidden)]
245pub type ContractFunctionF = dyn Send + Sync + Fn(Env, &[Val]) -> Val;
246#[doc(hidden)]
247pub trait ContractFunctionRegister {
248 fn register(name: &'static str, func: &'static ContractFunctionF);
249}
250#[doc(hidden)]
251pub trait ContractFunctionSet {
252 fn call(&self, func: &str, env: Env, args: &[Val]) -> Option<Val>;
253}
254
255#[doc(inline)]
256pub use crate::env::internal::LedgerInfo;
257
258pub(crate) fn default_ledger_info() -> LedgerInfo {
260 LedgerInfo {
261 protocol_version: 23,
262 sequence_number: 0,
263 timestamp: 0,
264 network_id: [0; 32],
265 base_reserve: 0,
266 min_persistent_entry_ttl: 4096,
267 min_temp_entry_ttl: 16,
268 max_entry_ttl: 6_312_000,
269 }
270}
271
272pub trait Ledger {
274 fn set(&self, l: LedgerInfo);
276
277 fn set_protocol_version(&self, protocol_version: u32);
279
280 fn set_sequence_number(&self, sequence_number: u32);
282
283 fn set_timestamp(&self, timestamp: u64);
285
286 fn set_network_id(&self, network_id: [u8; 32]);
288
289 fn set_base_reserve(&self, base_reserve: u32);
291
292 fn set_min_temp_entry_ttl(&self, min_temp_entry_ttl: u32);
294
295 fn set_min_persistent_entry_ttl(&self, min_persistent_entry_ttl: u32);
297
298 fn set_max_entry_ttl(&self, max_entry_ttl: u32);
300
301 fn get(&self) -> LedgerInfo;
303
304 fn with_mut<F>(&self, f: F)
306 where
307 F: FnMut(&mut LedgerInfo);
308}
309
310pub mod budget {
311 use core::fmt::{Debug, Display};
312
313 #[doc(inline)]
314 use crate::env::internal::budget::CostTracker;
315 #[doc(inline)]
316 pub use crate::xdr::ContractCostType;
317
318 pub struct Budget(pub(crate) crate::env::internal::budget::Budget);
346
347 impl Display for Budget {
348 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
349 writeln!(f, "{}", self.0)
350 }
351 }
352
353 impl Debug for Budget {
354 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
355 writeln!(f, "{:?}", self.0)
356 }
357 }
358
359 impl Budget {
360 pub(crate) fn new(b: crate::env::internal::budget::Budget) -> Self {
361 Self(b)
362 }
363
364 pub fn reset_default(&mut self) {
366 self.0.reset_default().unwrap();
367 }
368
369 pub fn reset_unlimited(&mut self) {
370 self.0.reset_unlimited().unwrap();
371 }
372
373 pub fn reset_limits(&mut self, cpu: u64, mem: u64) {
374 self.0.reset_limits(cpu, mem).unwrap();
375 }
376
377 pub fn reset_tracker(&mut self) {
378 self.0.reset_tracker().unwrap();
379 }
380
381 pub fn cpu_instruction_cost(&self) -> u64 {
386 self.0.get_cpu_insns_consumed().unwrap()
387 }
388
389 pub fn memory_bytes_cost(&self) -> u64 {
394 self.0.get_mem_bytes_consumed().unwrap()
395 }
396
397 pub fn tracker(&self, cost_type: ContractCostType) -> CostTracker {
406 self.0.get_tracker(cost_type).unwrap()
407 }
408
409 pub fn print(&self) {
411 println!("{}", self.0);
412 }
413 }
414}
415
416pub trait Events {
418 fn all(&self) -> Vec<(crate::Address, Vec<Val>, Val)>;
425}
426
427pub trait Logs {
429 fn all(&self) -> std::vec::Vec<String>;
431 fn print(&self);
433}
434
435pub trait BytesN<const N: usize> {
437 fn random(env: &Env) -> crate::BytesN<N>;
441}
442
443pub(crate) fn random<const N: usize>() -> [u8; N] {
447 use rand::RngCore;
448 let mut arr = [0u8; N];
449 rand::thread_rng().fill_bytes(&mut arr);
450 arr
451}
452
453pub trait Address {
454 fn generate(env: &Env) -> crate::Address;
460}
461
462pub trait MuxedAddress {
463 fn generate(env: &Env) -> crate::MuxedAddress;
468
469 fn new<T: Into<crate::MuxedAddress>>(address: T, id: u64) -> crate::MuxedAddress;
480}
481
482pub trait Deployer {
483 fn get_contract_instance_ttl(&self, contract: &crate::Address) -> u32;
491
492 fn get_contract_code_ttl(&self, contract: &crate::Address) -> u32;
500}
501
502pub use xdr::AccountFlags as IssuerFlags;
503
504#[derive(Clone)]
505pub struct StellarAssetIssuer {
506 env: Env,
507 account_id: xdr::AccountId,
508}
509
510impl StellarAssetIssuer {
511 pub(crate) fn new(env: Env, account_id: xdr::AccountId) -> Self {
512 Self { env, account_id }
513 }
514
515 pub fn flags(&self) -> u32 {
517 let k = Rc::new(xdr::LedgerKey::Account(xdr::LedgerKeyAccount {
518 account_id: self.account_id.clone(),
519 }));
520
521 let (entry, _) = self.env.host().get_ledger_entry(&k).unwrap().unwrap();
522
523 match &entry.data {
524 xdr::LedgerEntryData::Account(e) => e.flags,
525 _ => panic!("expected account entry but got {:?}", entry.data),
526 }
527 }
528
529 pub fn set_flag(&self, flag: IssuerFlags) {
531 self.overwrite_issuer_flags(self.flags() | (flag as u32))
532 }
533
534 pub fn clear_flag(&self, flag: IssuerFlags) {
536 self.overwrite_issuer_flags(self.flags() & (!(flag as u32)))
537 }
538
539 pub fn address(&self) -> crate::Address {
540 xdr::ScAddress::Account(self.account_id.clone())
541 .try_into_val(&self.env.clone())
542 .unwrap()
543 }
544
545 fn overwrite_issuer_flags(&self, flags: u32) {
550 if u64::from(flags) > xdr::MASK_ACCOUNT_FLAGS_V17 {
551 panic!(
552 "issuer flags value must be at most {}",
553 xdr::MASK_ACCOUNT_FLAGS_V17
554 );
555 }
556
557 let k = Rc::new(xdr::LedgerKey::Account(xdr::LedgerKeyAccount {
558 account_id: self.account_id.clone(),
559 }));
560
561 let (entry, _) = self.env.host().get_ledger_entry(&k).unwrap().unwrap();
562 let mut entry = entry.as_ref().clone();
563
564 match entry.data {
565 xdr::LedgerEntryData::Account(ref mut e) => e.flags = flags,
566 _ => panic!("expected account entry but got {:?}", entry.data),
567 }
568
569 self.env
570 .host()
571 .add_ledger_entry(&k, &Rc::new(entry), None)
572 .unwrap();
573 }
574}
575
576pub struct StellarAssetContract {
577 address: crate::Address,
578 issuer: StellarAssetIssuer,
579 asset: xdr::Asset,
580}
581
582impl StellarAssetContract {
583 pub(crate) fn new(
584 address: crate::Address,
585 issuer: StellarAssetIssuer,
586 asset: xdr::Asset,
587 ) -> Self {
588 Self {
589 address,
590 issuer,
591 asset,
592 }
593 }
594
595 pub fn address(&self) -> crate::Address {
596 self.address.clone()
597 }
598
599 pub fn issuer(&self) -> StellarAssetIssuer {
600 self.issuer.clone()
601 }
602
603 #[doc(hidden)]
604 pub fn asset(&self) -> xdr::Asset {
605 self.asset.clone()
606 }
607}