ink_sandbox/
macros.rs

1use std::time::SystemTime;
2
3use frame_support::{
4    sp_runtime::{
5        traits::{
6            Header,
7            One,
8        },
9        BuildStorage,
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. Optionally can take a chain
83/// extension type as a second argument.
84///
85/// The new macro will automatically implement `crate::Sandbox`.
86#[macro_export]
87macro_rules! create_sandbox {
88    ($name:ident) => {
89        $crate::paste::paste! {
90            $crate::create_sandbox!($name, [<$name Runtime>], (), (), {});
91        }
92    };
93    ($name:ident, $chain_extension: ty, $debug: ty) => {
94        $crate::paste::paste! {
95            $crate::create_sandbox!($name, [<$name Runtime>], $chain_extension, $debug, {});
96        }
97    };
98    ($name:ident, $chain_extension: ty, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
99        $crate::paste::paste! {
100            $crate::create_sandbox!($name, [<$name Runtime>], $chain_extension, $debug, {
101                $(
102                    $pallet_name : $pallet,
103                )*
104            });
105        }
106    };
107    ($sandbox:ident, $runtime:ident, $chain_extension: ty, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
108
109
110// Put all the boilerplate into an auxiliary module
111mod construct_runtime {
112
113    // Bring some common types into the scope
114    use $crate::frame_support::{
115        construct_runtime,
116        derive_impl,
117        parameter_types,
118        sp_runtime::{
119            traits::Convert,
120            AccountId32, Perbill,
121        },
122        traits::{ConstBool, ConstU128, ConstU32, ConstU64, Currency},
123        weights::Weight,
124    };
125
126    use $crate::Snapshot;
127
128    // Define the runtime type as a collection of pallets
129    construct_runtime!(
130        pub enum $runtime {
131            System: $crate::frame_system,
132            Balances: $crate::pallet_balances,
133            Timestamp: $crate::pallet_timestamp,
134            Revive: $crate::pallet_revive,
135            $(
136                $pallet_name: $pallet,
137            )*
138        }
139    );
140
141    // Configure pallet system
142    #[derive_impl($crate::frame_system::config_preludes::SolochainDefaultConfig as $crate::frame_system::DefaultConfig)]
143    impl $crate::frame_system::Config for $runtime {
144        type Block = $crate::frame_system::mocking::MockBlockU32<$runtime>;
145        type Version = ();
146        type BlockHashCount = ConstU32<250>;
147        type AccountData = $crate::pallet_balances::AccountData<<$runtime as $crate::pallet_balances::Config>::Balance>;
148    }
149
150    // Configure pallet balances
151    impl $crate::pallet_balances::Config for $runtime {
152        type RuntimeEvent = RuntimeEvent;
153        type WeightInfo = ();
154        type Balance = u128;
155        type DustRemoval = ();
156        type ExistentialDeposit = ConstU128<1>;
157        type AccountStore = System;
158        type ReserveIdentifier = [u8; 8];
159        type FreezeIdentifier = ();
160        type MaxLocks = ();
161        type MaxReserves = ();
162        type MaxFreezes = ();
163        type RuntimeHoldReason = RuntimeHoldReason;
164        type RuntimeFreezeReason = RuntimeFreezeReason;
165        type DoneSlashHandler = ();
166    }
167
168    // Configure pallet timestamp
169    impl $crate::pallet_timestamp::Config for $runtime {
170        type Moment = u64;
171        type OnTimestampSet = ();
172        type MinimumPeriod = ConstU64<1>;
173        type WeightInfo = ();
174    }
175
176    // Configure pallet revive
177    type BalanceOf = <Balances as Currency<AccountId32>>::Balance;
178    impl Convert<Weight, BalanceOf> for $runtime {
179        fn convert(w: Weight) -> BalanceOf {
180            w.ref_time().into()
181        }
182    }
183
184    parameter_types! {
185        // TODO can we delete some?
186        pub DeletionWeightLimit: Weight = Weight::zero();
187        pub DefaultDepositLimit: BalanceOf = 10_000_000;
188        pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
189        pub MaxDelegateDependencies: u32 = 32;
190    }
191
192    impl $crate::pallet_revive::Config for $runtime {
193        type AddressMapper = $crate::pallet_revive::AccountId32Mapper<Self>;
194        type ChainId = ConstU64<1>; // TODO
195        type NativeToEthRatio = ConstU32<100_000_000>;
196        type Time = Timestamp;
197        type Currency = Balances;
198        type RuntimeEvent = RuntimeEvent;
199        type RuntimeCall = RuntimeCall;
200        type DepositPerItem = ConstU128<1>;
201        type DepositPerByte = ConstU128<1>;
202        type WeightPrice = Self;
203        type WeightInfo = ();
204        // todo remove this + the $chain_extension variable
205        // type ChainExtension = $chain_extension;
206        type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
207        type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
208        type UnsafeUnstableInterface = ConstBool<true>;
209        type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
210        type RuntimeHoldReason = RuntimeHoldReason;
211        type UploadOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
212        type InstantiateOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
213        type EthGasEncoder = ();
214        type FindAuthor = ();
215        type Precompiles = (
216            // todo
217            //ERC20<Self, InlineIdConfig<0x120>, TrustBackedAssetsInstance>,
218            //ERC20<Self, InlineIdConfig<0x320>, PoolAssetsInstance>,
219            //XcmPrecompile<Self>,
220        );
221    }
222
223    // Implement `crate::Sandbox` trait
224
225    /// Default initial balance for the default account.
226    pub const INITIAL_BALANCE: u128 = 1_000_000_000_000_000;
227    pub const DEFAULT_ACCOUNT: AccountId32 = AccountId32::new([1u8; 32]);
228
229    pub struct $sandbox {
230        ext: $crate::TestExternalities,
231    }
232
233    impl ::std::default::Default for $sandbox {
234        fn default() -> Self {
235            let ext = $crate::macros::BlockBuilder::<$runtime>::new_ext(vec![(
236                DEFAULT_ACCOUNT,
237                INITIAL_BALANCE,
238            )]);
239            Self { ext }
240        }
241    }
242
243    impl $crate::Sandbox for $sandbox {
244        type Runtime = $runtime;
245
246        fn execute_with<T>(&mut self, execute: impl FnOnce() -> T) -> T {
247            self.ext.execute_with(execute)
248        }
249
250        fn dry_run<T>(&mut self, action: impl FnOnce(&mut Self) -> T) -> T {
251            // Make a backup of the backend.
252            let backend_backup = self.ext.as_backend();
253            // Run the action, potentially modifying storage. Ensure, that there are no pending changes
254            // that would affect the reverted backend.
255            let result = action(self);
256            self.ext.commit_all().expect("Failed to commit changes");
257
258            // Restore the backend.
259            self.ext.backend = backend_backup;
260            result
261        }
262
263        fn register_extension<E: ::core::any::Any + $crate::Extension>(&mut self, ext: E) {
264            self.ext.register_extension(ext);
265        }
266
267        fn initialize_block(
268            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
269            parent_hash: <Self::Runtime as $crate::frame_system::Config>::Hash,
270        ) {
271            $crate::macros::BlockBuilder::<Self::Runtime>::initialize_block(height, parent_hash)
272        }
273
274        fn finalize_block(
275            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
276        ) -> <Self::Runtime as $crate::frame_system::Config>::Hash {
277            $crate::macros::BlockBuilder::<Self::Runtime>::finalize_block(height)
278        }
279
280        fn default_actor() -> $crate::AccountIdFor<Self::Runtime> {
281            DEFAULT_ACCOUNT
282        }
283
284        fn get_metadata() -> $crate::RuntimeMetadataPrefixed {
285            Self::Runtime::metadata()
286        }
287
288        fn convert_account_to_origin(
289            account: $crate::AccountIdFor<Self::Runtime>,
290        ) -> <<Self::Runtime as $crate::frame_system::Config>::RuntimeCall as $crate::frame_support::sp_runtime::traits::Dispatchable>::RuntimeOrigin {
291            Some(account).into()
292        }
293
294        fn take_snapshot(&mut self) -> Snapshot {
295            let mut backend = self.ext.as_backend().clone();
296            let raw_key_values = backend
297                .backend_storage_mut()
298                .drain()
299                .into_iter()
300                .filter(|(_, (_, r))| *r > 0)
301                .collect::<Vec<(Vec<u8>, (Vec<u8>, i32))>>();
302            let root = backend.root().to_owned();
303            Snapshot {
304                storage: raw_key_values,
305                storage_root: root,
306            }
307        }
308
309        fn restore_snapshot(&mut self, snapshot: Snapshot) {
310            self.ext = $crate::TestExternalities::from_raw_snapshot(
311                snapshot.storage,
312                snapshot.storage_root,
313                Default::default(),
314            );
315        }
316    }
317}
318
319// Export runtime type itself, pallets and useful types from the auxiliary module
320pub use construct_runtime::{
321    $sandbox, $runtime, Balances, Revive, PalletInfo, RuntimeCall, RuntimeEvent, RuntimeHoldReason,
322    RuntimeOrigin, System, Timestamp,
323};
324    };
325}
326
327create_sandbox!(DefaultSandbox);