ink_sandbox/
macros.rs

1use std::time::SystemTime;
2
3use frame_support::{
4    sp_runtime::{
5        BuildStorage,
6        traits::{
7            Header,
8            One,
9        },
10    },
11    traits::Hooks,
12};
13use frame_system::pallet_prelude::BlockNumberFor;
14use sp_io::TestExternalities;
15
16/// A helper struct for initializing and finalizing blocks.
17pub struct BlockBuilder<T>(std::marker::PhantomData<T>);
18
19impl<
20    T: pallet_balances::Config
21        + pallet_timestamp::Config<Moment = u64>
22        + pallet_revive::Config,
23> BlockBuilder<T>
24{
25    /// Create a new externalities with the given balances.
26    pub fn new_ext(balances: Vec<(T::AccountId, T::Balance)>) -> TestExternalities {
27        let mut storage = frame_system::GenesisConfig::<T>::default()
28            .build_storage()
29            .unwrap();
30
31        pallet_balances::GenesisConfig::<T> {
32            balances,
33            dev_accounts: None,
34        }
35        .assimilate_storage(&mut storage)
36        .unwrap();
37
38        let mut ext = TestExternalities::new(storage);
39
40        ext.execute_with(|| {
41            Self::initialize_block(BlockNumberFor::<T>::one(), Default::default())
42        });
43        ext
44    }
45
46    /// Initialize a new block at particular height.
47    pub fn initialize_block(
48        height: frame_system::pallet_prelude::BlockNumberFor<T>,
49        parent_hash: <T as frame_system::Config>::Hash,
50    ) {
51        frame_system::Pallet::<T>::reset_events();
52        frame_system::Pallet::<T>::initialize(&height, &parent_hash, &Default::default());
53        pallet_balances::Pallet::<T>::on_initialize(height);
54        pallet_timestamp::Pallet::<T>::set_timestamp(
55            SystemTime::now()
56                .duration_since(SystemTime::UNIX_EPOCH)
57                .expect("Time went backwards")
58                .as_secs(),
59        );
60        pallet_timestamp::Pallet::<T>::on_initialize(height);
61        pallet_revive::Pallet::<T>::on_initialize(height);
62        frame_system::Pallet::<T>::note_finished_initialize();
63    }
64
65    /// Finalize a block at particular height.
66    pub fn finalize_block(
67        height: frame_system::pallet_prelude::BlockNumberFor<T>,
68    ) -> <T as frame_system::Config>::Hash {
69        pallet_revive::Pallet::<T>::on_finalize(height);
70        use sp_core::Get;
71        let minimum_period = <T as pallet_timestamp::Config>::MinimumPeriod::get();
72        let now = pallet_timestamp::Pallet::<T>::get()
73            .checked_add(minimum_period)
74            .unwrap();
75        pallet_timestamp::Pallet::<T>::set_timestamp(now);
76        pallet_timestamp::Pallet::<T>::on_finalize(height);
77        pallet_balances::Pallet::<T>::on_finalize(height);
78        frame_system::Pallet::<T>::finalize().hash()
79    }
80}
81
82/// Macro creating a minimal runtime with the given name.
83///
84/// The new macro will automatically implement `crate::Sandbox`.
85#[macro_export]
86macro_rules! create_sandbox {
87    ($name:ident) => {
88        $crate::paste::paste! {
89            $crate::create_sandbox!($name, [<$name Runtime>], (), {});
90        }
91    };
92    ($name:ident, $debug: ty) => {
93        $crate::paste::paste! {
94            $crate::create_sandbox!($name, [<$name Runtime>], $debug, {});
95        }
96    };
97    ($name:ident, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
98        $crate::paste::paste! {
99            $crate::create_sandbox!($name, [<$name Runtime>], $debug, {
100                $(
101                    $pallet_name : $pallet,
102                )*
103            });
104        }
105    };
106    ($sandbox:ident, $runtime:ident, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
107
108// Put all the boilerplate into an auxiliary module
109mod construct_runtime {
110
111    // Bring some common types into the scope
112    use $crate::frame_support::{
113        construct_runtime,
114        derive_impl,
115        parameter_types,
116        sp_runtime::{
117            traits::Convert,
118            AccountId32, Perbill,
119        },
120        traits::{ConstBool, ConstU128, ConstU32, ConstU64, Currency},
121        weights::Weight,
122    };
123
124    use $crate::Snapshot;
125
126    // Define the runtime type as a collection of pallets
127    construct_runtime!(
128        pub enum $runtime {
129            System: $crate::frame_system,
130            Balances: $crate::pallet_balances,
131            Timestamp: $crate::pallet_timestamp,
132            Revive: $crate::pallet_revive,
133            $(
134                $pallet_name: $pallet,
135            )*
136        }
137    );
138
139    // Configure pallet system
140    #[derive_impl($crate::frame_system::config_preludes::SolochainDefaultConfig as $crate::frame_system::DefaultConfig)]
141    impl $crate::frame_system::Config for $runtime {
142        type Block = $crate::frame_system::mocking::MockBlockU32<$runtime>;
143        type Version = ();
144        type BlockHashCount = ConstU32<250>;
145        type AccountData = $crate::pallet_balances::AccountData<<$runtime as $crate::pallet_balances::Config>::Balance>;
146    }
147
148    // Configure pallet balances
149    impl $crate::pallet_balances::Config for $runtime {
150        type RuntimeEvent = RuntimeEvent;
151        type WeightInfo = ();
152        type Balance = u128;
153        type DustRemoval = ();
154        type ExistentialDeposit = ConstU128<1>;
155        type AccountStore = System;
156        type ReserveIdentifier = [u8; 8];
157        type FreezeIdentifier = ();
158        type MaxLocks = ();
159        type MaxReserves = ();
160        type MaxFreezes = ();
161        type RuntimeHoldReason = RuntimeHoldReason;
162        type RuntimeFreezeReason = RuntimeFreezeReason;
163        type DoneSlashHandler = ();
164    }
165
166    // Configure pallet timestamp
167    impl $crate::pallet_timestamp::Config for $runtime {
168        type Moment = u64;
169        type OnTimestampSet = ();
170        type MinimumPeriod = ConstU64<1>;
171        type WeightInfo = ();
172    }
173
174    // Configure pallet revive
175    type BalanceOf = <Balances as Currency<AccountId32>>::Balance;
176    impl Convert<Weight, BalanceOf> for $runtime {
177        fn convert(w: Weight) -> BalanceOf {
178            w.ref_time().into()
179        }
180    }
181
182    parameter_types! {
183        // TODO can we delete some?
184        pub DeletionWeightLimit: Weight = Weight::zero();
185        pub DefaultDepositLimit: BalanceOf = 10_000_000;
186        pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
187        pub MaxDelegateDependencies: u32 = 32;
188    }
189
190    impl $crate::pallet_revive::Config for $runtime {
191        type AddressMapper = $crate::pallet_revive::AccountId32Mapper<Self>;
192        type ChainId = ConstU64<1>; // TODO
193        type NativeToEthRatio = ConstU32<100_000_000>;
194        type Time = Timestamp;
195        type Currency = Balances;
196        type RuntimeEvent = RuntimeEvent;
197        type RuntimeCall = RuntimeCall;
198        type DepositPerItem = ConstU128<1>;
199        type DepositPerByte = ConstU128<1>;
200        type WeightPrice = Self;
201        type WeightInfo = ();
202        type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
203        type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
204        type UnsafeUnstableInterface = ConstBool<true>;
205        type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
206        type RuntimeHoldReason = RuntimeHoldReason;
207        type UploadOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
208        type InstantiateOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
209        type EthGasEncoder = ();
210        type FindAuthor = ();
211        type Precompiles = (
212            // todo
213            //ERC20<Self, InlineIdConfig<0x120>, TrustBackedAssetsInstance>,
214            //ERC20<Self, InlineIdConfig<0x320>, PoolAssetsInstance>,
215            //XcmPrecompile<Self>,
216        );
217        type AllowEVMBytecode = ConstBool<false>;
218    }
219
220    // Implement `crate::Sandbox` trait
221
222    /// Default initial balance for the default account.
223    pub const INITIAL_BALANCE: u128 = 1_000_000_000_000_000;
224    pub const DEFAULT_ACCOUNT: AccountId32 = AccountId32::new([1u8; 32]);
225
226    pub struct $sandbox {
227        ext: $crate::TestExternalities,
228    }
229
230    impl ::std::default::Default for $sandbox {
231        fn default() -> Self {
232            let ext = $crate::macros::BlockBuilder::<$runtime>::new_ext(vec![(
233                DEFAULT_ACCOUNT,
234                INITIAL_BALANCE,
235            )]);
236            Self { ext }
237        }
238    }
239
240    impl $crate::Sandbox for $sandbox {
241        type Runtime = $runtime;
242
243        fn execute_with<T>(&mut self, execute: impl FnOnce() -> T) -> T {
244            self.ext.execute_with(execute)
245        }
246
247        fn dry_run<T>(&mut self, action: impl FnOnce(&mut Self) -> T) -> T {
248            // Make a backup of the backend.
249            let backend_backup = self.ext.as_backend();
250            // Run the action, potentially modifying storage. Ensure, that there are no pending changes
251            // that would affect the reverted backend.
252            let result = action(self);
253            self.ext.commit_all().expect("Failed to commit changes");
254
255            // Restore the backend.
256            self.ext.backend = backend_backup;
257            result
258        }
259
260        fn register_extension<E: ::core::any::Any + $crate::Extension>(&mut self, ext: E) {
261            self.ext.register_extension(ext);
262        }
263
264        fn initialize_block(
265            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
266            parent_hash: <Self::Runtime as $crate::frame_system::Config>::Hash,
267        ) {
268            $crate::macros::BlockBuilder::<Self::Runtime>::initialize_block(height, parent_hash)
269        }
270
271        fn finalize_block(
272            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
273        ) -> <Self::Runtime as $crate::frame_system::Config>::Hash {
274            $crate::macros::BlockBuilder::<Self::Runtime>::finalize_block(height)
275        }
276
277        fn default_actor() -> $crate::AccountIdFor<Self::Runtime> {
278            DEFAULT_ACCOUNT
279        }
280
281        fn get_metadata() -> $crate::RuntimeMetadataPrefixed {
282            Self::Runtime::metadata()
283        }
284
285        fn convert_account_to_origin(
286            account: $crate::AccountIdFor<Self::Runtime>,
287        ) -> <<Self::Runtime as $crate::frame_system::Config>::RuntimeCall as $crate::frame_support::sp_runtime::traits::Dispatchable>::RuntimeOrigin {
288            Some(account).into()
289        }
290
291        fn take_snapshot(&mut self) -> Snapshot {
292            let mut backend = self.ext.as_backend().clone();
293            let raw_key_values = backend
294                .backend_storage_mut()
295                .drain()
296                .into_iter()
297                .filter(|(_, (_, r))| *r > 0)
298                .collect::<Vec<(Vec<u8>, (Vec<u8>, i32))>>();
299            let root = backend.root().to_owned();
300            Snapshot {
301                storage: raw_key_values,
302                storage_root: root,
303            }
304        }
305
306        fn restore_snapshot(&mut self, snapshot: Snapshot) {
307            self.ext = $crate::TestExternalities::from_raw_snapshot(
308                snapshot.storage,
309                snapshot.storage_root,
310                Default::default(),
311            );
312        }
313    }
314}
315
316// Export runtime type itself, pallets and useful types from the auxiliary module
317pub use construct_runtime::{
318    $sandbox, $runtime, Balances, Revive, PalletInfo, RuntimeCall, RuntimeEvent, RuntimeHoldReason,
319    RuntimeOrigin, System, Timestamp,
320};
321    };
322}
323
324create_sandbox!(DefaultSandbox);