scrypto_test/environment/env.rs
1#![allow(clippy::test_attr_in_doctest)]
2
3//! This module defines and implements the [`TestEnvironment`] struct.
4
5use super::*;
6use crate::prelude::*;
7
8/// The environment that all tests of this testing framework are run against.
9///
10/// This struct may be thought of as the main struct in this testing framework which encapsulates a
11/// a self-contained instance of the Radix Engine ([`EncapsulatedRadixEngine`]). The functionality
12/// of the Radix Engine is exposed through the [`SystemApi`] which makes this testing environment no
13/// less capable than Scrypto code.
14///
15/// ## Introduction
16///
17/// This testing framework is designed to allow you to write Scrypto-like code and use that to test
18/// your packages and blueprints and follows a different approach from the `LedgerSimulator` class. The
19/// test-runner is an in-memory ledger simulator which you can interact with as a user that submits
20/// transactions to the network. The approach followed by this testing framework is different,
21/// instead of submitting transactions, you're making invocations to the Radix Engine, getting
22/// results back, and then writing assertions against what you got back.
23///
24/// Both the LedgerSimulator and this testing framework will prove to be useful throughout your blueprint
25/// development journey. As an example, this testing framework allows you to disable some of kernel
26/// modules that may get in your way when writing tests so it may be an optimal framework to use to
27/// ensure that the "math checks out" in your blueprint code without needing to think about costing
28/// or auth. However, when you're reaching the final stages of developing a blueprint you may want
29/// tests that check that interactions with your blueprint will succeed in a simulated setting that
30/// is close to the real setting, which is when the `LedgerSimulator` comes in. Overall, we may put these
31/// two frameworks into two categories: This framework (named scrypto-test) is a framework for unit
32/// testing your blueprints and is a good framework to use to check that your DeFi logic is correct.
33/// The `LedgerSimulator` is an integration testing or an end-to-end testing framework to test that your
34/// blueprints work in a simulated ledger with all of the costing limits, substate limits, and other
35/// limits applied.
36///
37/// ## Features
38///
39/// This framework has many new features that developers may find useful when testing their packages
40/// some of those features are:
41///
42/// * The ability to create mock [`Bucket`]s and [`Proof`]s through two main ways: by creating them
43/// out of thin air, and by disabling the auth module and minting them. This functionality can be
44/// found in the [`BucketFactory`] and [`ProofFactory`] structs and the [`CreationStrategy`].
45/// * The ability to query the contents of [`Bucket`]s and [`Proof`]s for the purpose of writing
46/// assertions against them. Not only that, but this testing framework allows you to call any
47/// method you wish on these nodes. As an example, in a test, you can get a [`Bucket`] and then
48/// create a proof out of it in manner similar to Scrypto.
49/// * The ability to enable and disable kernel modules at runtime. The Radix Engine kernel is quite
50/// modular with concepts such as auth, costing, and limits being implemented as kernel modules.
51/// Disabling or enabling kernel modules at runtime can prove to be quite useful when writing DeFi
52/// tests. As an example, you may want to not think about costing at all when writing tests and
53/// thus you may opt to disable the costing module entirely and continue your test without it.
54/// This can be done through [`TestEnvironment::disable_costing_module`].
55/// * This testing framework uses test bindings to provide a higher-level API for calling methods
56/// and functions on a blueprint without the need to do raw [`TestEnvironment::call_method_typed`]
57/// or [`TestEnvironment::call_function_typed`]. The test bindings are generated by the blueprint
58/// macro and are feature gated behind a `test` feature.
59///
60/// ## Getting Started
61///
62/// The following example shows a very simple test that gets XRD from the faucet and then asserts
63/// that the amount is equal to what we expect.
64///
65/// ```
66/// use scrypto_test::prelude::*;
67///
68/// fn my_test() -> Result<(), RuntimeError> {
69/// // Arrange
70/// let mut env = TestEnvironment::new();
71///
72/// // Act
73/// let bucket = env.call_method_typed::<_, _, Bucket>(
74/// FAUCET,
75/// "free",
76/// &(),
77/// )?;
78///
79/// // Assert
80/// let amount = bucket.amount(&mut env)?;
81/// assert_eq!(amount, dec!("10000"));
82///
83/// Ok(())
84/// }
85/// ```
86///
87/// A few things to note about the code you see above:
88///
89/// * There is no transactions, worktop, receipt, manifests or anything of that sort! This part is
90/// "not just hidden" from this testing framework but is actually non existent! The approach that
91/// framework of wrapping a self-contained Radix Engine means that there is no need for manifests
92/// or other transaction concepts.
93/// * Methods such as [`Bucket::amount`] can be called to get the amount of resources in a bucket
94/// and then assert against that.
95///
96/// ## Manipulating Kernel Modules
97///
98/// At runtime, the kernel modules can be enabled or disabled. For each kernel module there are four
99/// methods on the [`TestEnvironment`]:
100///
101/// * A method to enable the kernel module (e.g., [`TestEnvironment::enable_costing_module`]).
102/// * A method to disable the kernel module (e.g., [`TestEnvironment::disable_costing_module`]).
103/// * A method to perform some action in a callback with the module enabled (e.g.,
104/// [`TestEnvironment::with_costing_module_enabled`]).
105/// * A method to perform some action in a callback with the module disabled (e.g.,
106/// [`TestEnvironment::with_costing_module_disabled`]).
107///
108/// The simple enable and disable methods are quite straightforward: call them to enable or disable
109/// a kernel module. The `with_*` methods are a little bit more intricate, they allow you to perform
110/// some actions with a specific kernel either enabled or disabled and then resets the state of the
111/// kernel modules afterwards. As an example:
112///
113/// ```
114/// use scrypto_test::prelude::*;
115///
116/// // Arrange
117/// let mut env = TestEnvironment::new();
118///
119/// // Act
120/// let bucket = env.with_auth_module_disabled(|env| {
121/// /* Auth Module is disabled just before this point */
122/// ResourceManager(XRD).mint_fungible(100.into(), env)
123/// /* Kernel modules are reset just after this point. */
124/// })?;
125///
126/// // Assert
127/// let amount = bucket.amount(&mut env)?;
128/// assert_eq!(amount, dec!("100"));
129///
130/// # Ok::<(), RuntimeError>(())
131/// ```
132///
133/// ## Common Arranges or Teardowns
134///
135/// There are cases where you may have many tests that all share a large portion of your arrange
136/// or teardown logic. While this framework does not specifically provide solutions for this, there
137/// are many useful Rust patterns that may be employed here to allow you to do this: the simplest
138/// and the most elegant is probably by using callback functions.
139///
140/// Imagine this, you're building a Dex and many of the tests you write require you to have two
141/// resources with a very large supply so you can write your tests with. You can achieve this by
142/// doing something like:
143///
144/// ```
145/// use scrypto_test::prelude::*;
146///
147/// pub fn two_resource_environment<F>(func: F)
148/// where
149/// F: FnOnce(DefaultTestEnvironment, FungibleBucket, FungibleBucket),
150/// {
151/// let mut env = TestEnvironment::new();
152/// let bucket1 = ResourceBuilder::new_fungible(OwnerRole::None)
153/// .mint_initial_supply(dec!("100000000000"), &mut env)
154/// .unwrap();
155/// let bucket2 = ResourceBuilder::new_fungible(OwnerRole::None)
156/// .mint_initial_supply(dec!("100000000000"), &mut env)
157/// .unwrap();
158/// func(env, bucket1, bucket2)
159///
160/// /* Potential teardown happens here */
161/// }
162///
163/// #[test]
164/// fn contribution_provides_expected_amount_of_pool_units() {
165/// two_resource_environment(|mut env, bucket1, bucket2| {
166/// /* Your test goes here */
167/// })
168/// }
169/// ```
170///
171/// You may have a function like `two_resource_environment` seen above which sets up the environment
172/// and then some callback and potentially then executes some teardown code. Another way to do this
173/// would be through simple factory and destructor methods.
174/// ```
175pub struct TestEnvironment<D>(pub(super) EncapsulatedRadixEngine<D>)
176where
177 D: SubstateDatabase + CommittableSubstateDatabase + 'static;
178
179pub type DefaultTestEnvironment = TestEnvironment<InMemorySubstateDatabase>;
180
181impl DefaultTestEnvironment {
182 pub fn new() -> Self {
183 TestEnvironmentBuilder::new().build()
184 }
185}
186
187impl<D> TestEnvironment<D>
188where
189 D: SubstateDatabase + CommittableSubstateDatabase + 'static,
190{
191 // ===================
192 // region:invocations
193 // ===================
194
195 /// Invokes a function on the provided blueprint and package with the given arguments.
196 ///
197 /// This method is a typed version of the [`SystemBlueprintApi::call_function`] which Scrypto
198 /// encodes the arguments and Scrypto decodes the returns on behalf of the caller. This method
199 /// assumes that the caller is correct about the argument and return types and panics if the
200 /// encoding or decoding fails.
201 ///
202 /// # Arguments
203 ///
204 /// * `package_address`: [`PackageAddress`] - The address of the package that contains the
205 /// blueprint.
206 /// * `blueprint_name`: [`&str`] - The name of the blueprint.
207 /// * `function_name`: [`&str`] - The nae of the function.
208 /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
209 /// is fulfilled by any type that implements [`ScryptoEncode`].
210 ///
211 /// # Returns
212 ///
213 /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
214 /// was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
215 /// [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
216 /// [`ScryptoDecode`].
217 ///
218 /// # Panics
219 ///
220 /// This method panics in the following two cases:
221 ///
222 /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
223 /// the SBOR documentation on more information on why SBOR encoding may fail.
224 /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
225 /// could be decoded as the desired output type.
226 pub fn call_function_typed<I, O>(
227 &mut self,
228 package_address: PackageAddress,
229 blueprint_name: &str,
230 function_name: &str,
231 args: &I,
232 ) -> Result<O, RuntimeError>
233 where
234 I: ScryptoEncode,
235 O: ScryptoDecode,
236 {
237 let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
238 self.call_function(package_address, blueprint_name, function_name, args)
239 .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
240 }
241
242 /// Invokes a method on the main module of a node with the provided typed arguments.
243 ///
244 /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
245 /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
246 /// that the caller is correct about the argument and return types and panics if the encoding or
247 /// decoding fails.
248 ///
249 /// # Arguments
250 ///
251 /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
252 /// fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
253 /// * `method_name`: [`&str`] - The name of the method to invoke.
254 /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
255 /// is fulfilled by any type that implements [`ScryptoEncode`].
256 ///
257 /// # Returns
258 ///
259 /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
260 /// was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
261 /// [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
262 /// [`ScryptoDecode`].
263 ///
264 /// # Panics
265 ///
266 /// This method panics in the following two cases:
267 ///
268 /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
269 /// the SBOR documentation on more information on why SBOR encoding may fail.
270 /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
271 /// could be decoded as the desired output type.
272 pub fn call_method_typed<N, I, O>(
273 &mut self,
274 node_id: N,
275 method_name: &str,
276 args: &I,
277 ) -> Result<O, RuntimeError>
278 where
279 N: Into<NodeId>,
280 I: ScryptoEncode,
281 O: ScryptoDecode,
282 {
283 let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
284 self.call_method(&node_id.into(), method_name, args)
285 .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
286 }
287
288 /// Invokes a method on the main module of a node with the provided typed arguments.
289 ///
290 /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
291 /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
292 /// that the caller is correct about the argument and return types and panics if the encoding or
293 /// decoding fails.
294 ///
295 /// # Arguments
296 ///
297 /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
298 /// fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
299 /// * `method_name`: [`&str`] - The name of the method to invoke.
300 /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
301 /// is fulfilled by any type that implements [`ScryptoEncode`].
302 ///
303 /// # Returns
304 ///
305 /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
306 /// was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
307 /// [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
308 /// [`ScryptoDecode`].
309 ///
310 /// # Panics
311 ///
312 /// This method panics in the following two cases:
313 ///
314 /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
315 /// the SBOR documentation on more information on why SBOR encoding may fail.
316 /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
317 /// could be decoded as the desired output type.
318 pub fn call_direct_access_method_typed<N, I, O>(
319 &mut self,
320 node_id: N,
321 method_name: &str,
322 args: &I,
323 ) -> Result<O, RuntimeError>
324 where
325 N: Into<NodeId>,
326 I: ScryptoEncode,
327 O: ScryptoDecode,
328 {
329 let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
330 self.call_direct_access_method(&node_id.into(), method_name, args)
331 .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
332 }
333
334 /// Invokes a method on a module of a node with the provided typed arguments.
335 ///
336 /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
337 /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
338 /// that the caller is correct about the argument and return types and panics if the encoding or
339 /// decoding fails.
340 ///
341 /// # Arguments
342 ///
343 /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
344 /// fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
345 /// * `module`: [`AttachedModuleId`] - The module id.
346 /// * `method_name`: [`&str`] - The name of the method to invoke.
347 /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
348 /// is fulfilled by any type that implements [`ScryptoEncode`].
349 ///
350 /// # Returns
351 ///
352 /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
353 /// was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
354 /// [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
355 /// [`ScryptoDecode`].
356 ///
357 /// # Panics
358 ///
359 /// This method panics in the following two cases:
360 ///
361 /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
362 /// the SBOR documentation on more information on why SBOR encoding may fail.
363 /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
364 /// could be decoded as the desired output type.
365 pub fn call_module_method_typed<N, I, O>(
366 &mut self,
367 node_id: N,
368 module: AttachedModuleId,
369 method_name: &str,
370 args: &I,
371 ) -> Result<O, RuntimeError>
372 where
373 N: Into<NodeId>,
374 I: ScryptoEncode,
375 O: ScryptoDecode,
376 {
377 let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
378 self.call_module_method(&node_id.into(), module, method_name, args)
379 .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
380 }
381
382 //=======================
383 // endregion:invocations
384 // region:kernel-access
385 //=======================
386
387 pub fn with_kernel<F, O>(&mut self, callback: F) -> O
388 where
389 F: FnOnce(&TestKernel<'_, D>) -> O,
390 {
391 self.0.with_kernel(callback)
392 }
393
394 pub fn with_kernel_mut<F, O>(&mut self, callback: F) -> O
395 where
396 F: FnOnce(&mut TestKernel<'_, D>) -> O,
397 {
398 self.0.with_kernel_mut(callback)
399 }
400
401 //=========================
402 // endregion:kernel-access
403 // region:kernel-modules
404 //=========================
405
406 /// Enables the kernel trace kernel module of the Radix Engine.
407 pub fn enable_kernel_trace_module(&mut self) {
408 self.enable_module(EnabledModules::KERNEL_TRACE)
409 }
410
411 /// Enables the limits kernel module of the Radix Engine.
412 pub fn enable_limits_module(&mut self) {
413 self.enable_module(EnabledModules::LIMITS)
414 }
415
416 /// Enables the costing kernel module of the Radix Engine.
417 pub fn enable_costing_module(&mut self) {
418 self.enable_module(EnabledModules::COSTING)
419 }
420
421 /// Enables the auth kernel module of the Radix Engine.
422 pub fn enable_auth_module(&mut self) {
423 self.enable_module(EnabledModules::AUTH)
424 }
425
426 /// Enables the transaction env kernel module of the Radix Engine.
427 pub fn enable_transaction_runtime_module(&mut self) {
428 self.enable_module(EnabledModules::TRANSACTION_RUNTIME)
429 }
430
431 /// Enables the execution trace kernel module of the Radix Engine.
432 pub fn enable_execution_trace_module(&mut self) {
433 self.enable_module(EnabledModules::EXECUTION_TRACE)
434 }
435
436 /// Disables the kernel trace kernel module of the Radix Engine.
437 pub fn disable_kernel_trace_module(&mut self) {
438 self.disable_module(EnabledModules::KERNEL_TRACE)
439 }
440
441 /// Disables the limits kernel module of the Radix Engine.
442 pub fn disable_limits_module(&mut self) {
443 self.disable_module(EnabledModules::LIMITS)
444 }
445
446 /// Disables the costing kernel module of the Radix Engine.
447 pub fn disable_costing_module(&mut self) {
448 self.disable_module(EnabledModules::COSTING)
449 }
450
451 /// Disables the auth kernel module of the Radix Engine.
452 pub fn disable_auth_module(&mut self) {
453 self.disable_module(EnabledModules::AUTH)
454 }
455
456 /// Disables the transaction env kernel module of the Radix Engine.
457 pub fn disable_transaction_runtime_module(&mut self) {
458 self.disable_module(EnabledModules::TRANSACTION_RUNTIME)
459 }
460
461 /// Disables the execution trace kernel module of the Radix Engine.
462 pub fn disable_execution_trace_module(&mut self) {
463 self.disable_module(EnabledModules::EXECUTION_TRACE)
464 }
465
466 /// Calls the passed `callback` with the kernel trace kernel module enabled and then resets the
467 /// state of the kernel modules.
468 pub fn with_kernel_trace_module_enabled<F, O>(&mut self, callback: F) -> O
469 where
470 F: FnOnce(&mut Self) -> O,
471 {
472 let enabled_modules = self.enabled_modules();
473 self.enable_kernel_trace_module();
474 let rtn = callback(self);
475 self.set_enabled_modules(enabled_modules);
476 rtn
477 }
478
479 /// Calls the passed `callback` with the limits kernel module enabled and then resets the state
480 /// of the kernel modules.
481 pub fn with_limits_module_enabled<F, O>(&mut self, callback: F) -> O
482 where
483 F: FnOnce(&mut Self) -> O,
484 {
485 let enabled_modules = self.enabled_modules();
486 self.enable_limits_module();
487 let rtn = callback(self);
488 self.set_enabled_modules(enabled_modules);
489 rtn
490 }
491
492 /// Calls the passed `callback` with the costing kernel module enabled and then resets the state
493 /// of the kernel modules.
494 pub fn with_costing_module_enabled<F, O>(&mut self, callback: F) -> O
495 where
496 F: FnOnce(&mut Self) -> O,
497 {
498 let enabled_modules = self.enabled_modules();
499 self.enable_costing_module();
500 let rtn = callback(self);
501 self.set_enabled_modules(enabled_modules);
502 rtn
503 }
504
505 /// Calls the passed `callback` with the auth kernel module enabled and then resets the state of
506 /// the kernel modules.
507 pub fn with_auth_module_enabled<F, O>(&mut self, callback: F) -> O
508 where
509 F: FnOnce(&mut Self) -> O,
510 {
511 let enabled_modules = self.enabled_modules();
512 self.enable_auth_module();
513 let rtn = callback(self);
514 self.set_enabled_modules(enabled_modules);
515 rtn
516 }
517
518 /// Calls the passed `callback` with the transaction env kernel module enabled and then
519 /// resets the state of the kernel modules.
520 pub fn with_transaction_runtime_module_enabled<F, O>(&mut self, callback: F) -> O
521 where
522 F: FnOnce(&mut Self) -> O,
523 {
524 let enabled_modules = self.enabled_modules();
525 self.enable_transaction_runtime_module();
526 let rtn = callback(self);
527 self.set_enabled_modules(enabled_modules);
528 rtn
529 }
530
531 /// Calls the passed `callback` with the execution trace kernel module enabled and then resets
532 /// the state of the kernel modules.
533 pub fn with_execution_trace_module_enabled<F, O>(&mut self, callback: F) -> O
534 where
535 F: FnOnce(&mut Self) -> O,
536 {
537 let enabled_modules = self.enabled_modules();
538 self.enable_execution_trace_module();
539 let rtn = callback(self);
540 self.set_enabled_modules(enabled_modules);
541 rtn
542 }
543
544 /// Calls the passed `callback` with the kernel trace kernel module disabled and then resets the
545 /// state of the kernel modules.
546 pub fn with_kernel_trace_module_disabled<F, O>(&mut self, callback: F) -> O
547 where
548 F: FnOnce(&mut Self) -> O,
549 {
550 let enabled_modules = self.enabled_modules();
551 self.disable_kernel_trace_module();
552 let rtn = callback(self);
553 self.set_enabled_modules(enabled_modules);
554 rtn
555 }
556
557 /// Calls the passed `callback` with the limits kernel module disabled and then resets the state
558 /// of the kernel modules.
559 pub fn with_limits_module_disabled<F, O>(&mut self, callback: F) -> O
560 where
561 F: FnOnce(&mut Self) -> O,
562 {
563 let enabled_modules = self.enabled_modules();
564 self.disable_limits_module();
565 let rtn = callback(self);
566 self.set_enabled_modules(enabled_modules);
567 rtn
568 }
569
570 /// Calls the passed `callback` with the costing kernel module disabled and then resets the
571 /// state of the kernel modules.
572 pub fn with_costing_module_disabled<F, O>(&mut self, callback: F) -> O
573 where
574 F: FnOnce(&mut Self) -> O,
575 {
576 let enabled_modules = self.enabled_modules();
577 self.disable_costing_module();
578 let rtn = callback(self);
579 self.set_enabled_modules(enabled_modules);
580 rtn
581 }
582
583 /// Calls the passed `callback` with the auth kernel module disabled and then resets the state
584 /// of the kernel modules.
585 pub fn with_auth_module_disabled<F, O>(&mut self, callback: F) -> O
586 where
587 F: FnOnce(&mut Self) -> O,
588 {
589 let enabled_modules = self.enabled_modules();
590 self.disable_auth_module();
591 let rtn = callback(self);
592 self.set_enabled_modules(enabled_modules);
593 rtn
594 }
595
596 /// Calls the passed `callback` with the transaction env kernel module disabled and then
597 /// resets the state of the kernel modules.
598 pub fn with_transaction_runtime_module_disabled<F, O>(&mut self, callback: F) -> O
599 where
600 F: FnOnce(&mut Self) -> O,
601 {
602 let enabled_modules = self.enabled_modules();
603 self.disable_transaction_runtime_module();
604 let rtn = callback(self);
605 self.set_enabled_modules(enabled_modules);
606 rtn
607 }
608
609 /// Calls the passed `callback` with the execution trace kernel module disabled and then resets
610 /// the state of the kernel modules.
611 pub fn with_execution_trace_module_disabled<F, O>(&mut self, callback: F) -> O
612 where
613 F: FnOnce(&mut Self) -> O,
614 {
615 let enabled_modules = self.enabled_modules();
616 self.disable_execution_trace_module();
617 let rtn = callback(self);
618 self.set_enabled_modules(enabled_modules);
619 rtn
620 }
621
622 /// Returns the bit flags representing the currently enabled kernel modules.
623 pub fn enabled_modules(&self) -> EnabledModules {
624 self.0
625 .with_kernel(|kernel| kernel.kernel_callback().modules.enabled_modules)
626 }
627
628 /// Sets the bit flags representing the enabled kernel modules.
629 pub fn set_enabled_modules(&mut self, enabled_modules: EnabledModules) {
630 self.0.with_kernel_mut(|kernel| {
631 kernel.kernel_callback_mut().modules.enabled_modules = enabled_modules
632 })
633 }
634
635 /// Enables specific kernel module(s).
636 pub fn enable_module(&mut self, module: EnabledModules) {
637 self.0.with_kernel_mut(|kernel| {
638 kernel.kernel_callback_mut().modules.enabled_modules |= module
639 })
640 }
641
642 /// Disables specific kernel module(s).
643 pub fn disable_module(&mut self, module: EnabledModules) {
644 self.0.with_kernel_mut(|kernel| {
645 kernel.kernel_callback_mut().modules.enabled_modules &= !module
646 })
647 }
648
649 //==========================
650 // endregion:kernel-modules
651 // region:state
652 //==========================
653
654 /// Reads the state of a component and allows for a callback to be executed over the decoded
655 /// state.
656 ///
657 /// This method performs the steps needed to read the state of a component and then perform the
658 /// various steps needed before the state can be read or used such as the locking, reading, and
659 /// decoding of the substate and the various steps that need to be performed after the state is
660 /// read such as unlocking the substate.
661 ///
662 /// Users of this method are expected to pass in a callback function to operate over the state
663 /// as this is the main way that this method ensures that references do not escape out of this
664 /// method after the substate is closed.
665 ///
666 /// # Arguments
667 ///
668 /// * `node_id`: [`N`] - The address of the component to read the state of. This is a generic
669 /// type parameter that's satisfied by any type that implements [`Into<NodeId>`].
670 /// * `callback`: [`F`] - A callback function to call after the component state has been read
671 /// and decoded into the type specified by the generic parameter [`S`]. Anything returned from
672 /// this callback is returned from this method unless an error happens after the callback is
673 /// executed.
674 ///
675 /// # Returns
676 ///
677 /// * [`Result<O, RuntimeError>`] - The output of the callback function passed in or a runtime
678 /// error if one of the steps failed.
679 ///
680 /// # Panics
681 ///
682 /// This method panics if the component state can not be decoded as the generic type parameter
683 /// [`S`].
684 pub fn with_component_state<S, N, F, O>(
685 &mut self,
686 node_id: N,
687 mut callback: F,
688 ) -> Result<O, RuntimeError>
689 where
690 S: ScryptoDecode,
691 N: Into<NodeId>,
692 F: FnMut(&mut S, &mut Self) -> O,
693 {
694 let (handle, state) = self.0.with_kernel_mut(|kernel| {
695 // Lock
696 let handle = kernel.kernel_open_substate(
697 &node_id.into(),
698 MAIN_BASE_PARTITION,
699 &SubstateKey::Field(ComponentField::State0.into()),
700 LockFlags::read_only(),
701 SystemLockData::Field(FieldLockData::Read),
702 )?;
703
704 // Read
705 let state = kernel.kernel_read_substate(handle).map(|v| {
706 let FieldSubstate::<ScryptoValue>::V1(FieldSubstateV1 { payload, .. }) =
707 v.as_typed().unwrap();
708 scrypto_encode(&payload).unwrap()
709 })?;
710
711 Ok::<_, RuntimeError>((handle, state))
712 })?;
713
714 // Decode
715 let mut state = scrypto_decode::<S>(&state).unwrap();
716
717 // Callback
718 let rtn = callback(&mut state, self);
719
720 // Unlock
721 self.0
722 .with_kernel_mut(|kernel| kernel.kernel_close_substate(handle))?;
723
724 Ok(rtn)
725 }
726
727 //========================
728 // endregion:state
729 // region:epoch×tamp
730 //========================
731
732 /// Gets the current epoch from the Consensus Manager.
733 pub fn get_current_epoch(&mut self) -> Epoch {
734 Runtime::current_epoch(self).unwrap()
735 }
736
737 /// Sets the current epoch.
738 pub fn set_current_epoch(&mut self, epoch: Epoch) {
739 self.as_method_actor(
740 CONSENSUS_MANAGER,
741 ModuleId::Main,
742 CONSENSUS_MANAGER_NEXT_ROUND_IDENT,
743 |env| -> Result<(), RuntimeError> {
744 let manager_handle = env
745 .actor_open_field(
746 ACTOR_STATE_SELF,
747 ConsensusManagerField::State.into(),
748 LockFlags::MUTABLE,
749 )
750 .unwrap();
751 let mut manager_substate =
752 env.field_read_typed::<VersionedConsensusManagerState>(manager_handle)?;
753
754 manager_substate.as_unique_version_mut().epoch = epoch;
755
756 env.field_write_typed(manager_handle, &manager_substate)?;
757 env.field_close(manager_handle)?;
758 Ok(())
759 },
760 )
761 .unwrap()
762 .unwrap();
763 }
764
765 /// Gets the current time stamp from the Consensus Manager.
766 pub fn get_current_time(&mut self) -> Instant {
767 Runtime::current_time(TimePrecision::Second, self).unwrap()
768 }
769
770 pub fn set_current_time(&mut self, instant: Instant) {
771 self.as_method_actor(
772 CONSENSUS_MANAGER,
773 ModuleId::Main,
774 CONSENSUS_MANAGER_NEXT_ROUND_IDENT,
775 |env| -> Result<(), RuntimeError> {
776 let handle = env.actor_open_field(
777 ACTOR_STATE_SELF,
778 ConsensusManagerField::ProposerMinuteTimestamp.into(),
779 LockFlags::MUTABLE,
780 )?;
781 let mut proposer_minute_timestamp =
782 env.field_read_typed::<ConsensusManagerProposerMinuteTimestampFieldPayload>(
783 handle,
784 )?;
785 proposer_minute_timestamp
786 .as_unique_version_mut()
787 .epoch_minute =
788 i32::try_from(instant.seconds_since_unix_epoch / 60).map_err(|_| {
789 RuntimeError::ApplicationError(ApplicationError::ConsensusManagerError(
790 ConsensusManagerError::InvalidProposerTimestampUpdate {
791 from_millis: env.get_current_time().seconds_since_unix_epoch * 1000,
792 to_millis: instant.seconds_since_unix_epoch * 1000,
793 },
794 ))
795 })?;
796 env.field_write_typed(handle, &proposer_minute_timestamp)?;
797 env.field_close(handle)?;
798
799 let handle = env.actor_open_field(
800 ACTOR_STATE_SELF,
801 ConsensusManagerField::ProposerMilliTimestamp.into(),
802 LockFlags::MUTABLE,
803 )?;
804 let mut proposer_milli_timestamp =
805 env.field_read_typed::<ConsensusManagerProposerMilliTimestampFieldPayload>(
806 handle,
807 )?;
808 proposer_milli_timestamp.as_unique_version_mut().epoch_milli =
809 instant.seconds_since_unix_epoch * 1000;
810 env.field_write_typed(handle, &proposer_milli_timestamp)?;
811 env.field_close(handle)?;
812
813 Ok(())
814 },
815 )
816 .unwrap()
817 .unwrap();
818 }
819
820 //===========================
821 // endregion:epoch×tamp
822 // region:helpers
823 //===========================
824
825 /// Allows us to perform some action as another actor.
826 ///
827 /// This function pushes a new call-frame onto the stack with the actor information we desire,
828 /// performs the call-back, and then pops the call-frame off.
829 pub(crate) fn as_method_actor<N, F, O>(
830 &mut self,
831 node_id: N,
832 module_id: ModuleId,
833 method_ident: &str,
834 callback: F,
835 ) -> Result<O, RuntimeError>
836 where
837 N: Into<NodeId> + Copy,
838 F: FnOnce(&mut Self) -> O,
839 O: ScryptoEncode,
840 {
841 let object_info = self.0.with_kernel_mut(|kernel| {
842 SystemService::new(kernel).get_object_info(&node_id.into())
843 })?;
844 let auth_zone = self.0.with_kernel_mut(|kernel| {
845 let mut system_service = SystemService::new(kernel);
846 AuthModule::on_call_fn_mock(
847 &mut system_service,
848 Some((&node_id.into(), false)),
849 Default::default(),
850 Default::default(),
851 )
852 })?;
853 let actor = Actor::Method(MethodActor {
854 method_type: match module_id {
855 ModuleId::Main => MethodType::Main,
856 ModuleId::Royalty => MethodType::Module(AttachedModuleId::Royalty),
857 ModuleId::Metadata => MethodType::Module(AttachedModuleId::Metadata),
858 ModuleId::RoleAssignment => MethodType::Module(AttachedModuleId::RoleAssignment),
859 },
860 ident: method_ident.to_owned(),
861 node_id: node_id.into(),
862 auth_zone,
863 object_info,
864 });
865 self.as_actor(actor, callback)
866 }
867
868 /// Allows us to perform some action as another actor.
869 ///
870 /// This function pushes a new call-frame onto the stack with the actor information we desire,
871 /// performs the call-back, and then pops the call-frame off.
872 pub(crate) fn as_actor<F, O>(&mut self, actor: Actor, callback: F) -> Result<O, RuntimeError>
873 where
874 F: FnOnce(&mut Self) -> O,
875 O: ScryptoEncode,
876 {
877 // Creating the next frame.
878 let mut message =
879 CallFrameMessage::from_input(&IndexedScryptoValue::from_typed(&()), &actor);
880 self.0.with_kernel_mut(|kernel| {
881 let (substate_io, current_frame) = kernel.kernel_current_frame_mut();
882 message
883 .copy_global_references
884 .extend(current_frame.stable_references().keys());
885 let new_frame =
886 CallFrame::new_child_from_parent(substate_io, current_frame, actor, message)
887 .unwrap();
888 let old = core::mem::replace(current_frame, new_frame);
889 kernel.kernel_prev_frame_stack_mut().push(old);
890 });
891
892 // Executing the callback
893 let rtn = callback(self);
894
895 // Constructing the message from the returns
896 let message = {
897 let indexed_scrypto_value = IndexedScryptoValue::from_typed(&rtn);
898 CallFrameMessage {
899 move_nodes: indexed_scrypto_value.owned_nodes().clone(),
900 copy_global_references: indexed_scrypto_value.references().clone(),
901 ..Default::default()
902 }
903 };
904
905 // Popping the last frame & Passing message
906 self.0
907 .with_kernel_mut(|kernel| -> Result<(), RuntimeError> {
908 let mut previous_frame = kernel.kernel_prev_frame_stack_mut().pop().unwrap();
909 let (substate_io, current_frame) = kernel.kernel_current_frame_mut();
910
911 CallFrame::pass_message(
912 substate_io,
913 current_frame,
914 &mut previous_frame,
915 message.clone(),
916 )
917 .map_err(CallFrameError::PassMessageError)
918 .map_err(KernelError::CallFrameError)?;
919
920 *current_frame = previous_frame;
921 Ok(())
922 })?;
923
924 Ok(rtn)
925 }
926
927 //===================
928 // endregion:helpers
929 //===================
930}
931
932impl Default for TestEnvironment<InMemorySubstateDatabase> {
933 fn default() -> Self {
934 Self::new()
935 }
936}
937
938#[cfg(test)]
939mod tests {
940 use crate::prelude::*;
941
942 #[test]
943 pub fn test_env_can_be_created() {
944 let _ = TestEnvironment::new();
945 }
946
947 #[test]
948 pub fn test_time_set() {
949 // Arrange
950 let mut env = TestEnvironment::new();
951 let mut current_time = env.get_current_time();
952 current_time.seconds_since_unix_epoch += 60; // add 1 minute
953
954 // Act
955 env.set_current_time(current_time);
956
957 // Assert
958 let t1 = Runtime::current_time(TimePrecision::Second, &mut env).unwrap();
959 let t2 = Runtime::current_time(TimePrecision::Minute, &mut env).unwrap();
960
961 assert_eq!(t1, t2)
962 }
963}