domain_test_utils/
test_ethereum.rs

1//! Test setup shared by sp-domains-fraud-proof and domain-operator tests
2
3use crate::test_ethereum_tx::{
4    AccountInfo, EIP1559UnsignedTransaction, EIP2930UnsignedTransaction, LegacyUnsignedTransaction,
5};
6use domain_runtime_primitives::EthereumAccountId;
7use ethereum::TransactionV3 as Transaction;
8use frame_support::pallet_prelude::DispatchClass;
9use frame_system::pallet_prelude::RuntimeCallFor;
10use hex_literal::hex;
11use pallet_evm::GasWeightMapping;
12use sp_core::{Get, H160, U256};
13use sp_domains::PermissionedActionAllowedBy;
14
15/// The kind of account list to generate.
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
17pub enum EvmAccountList {
18    Anyone,
19    NoOne,
20    One,
21    Multiple,
22}
23
24/// Generate the supplied kind of account list.
25pub fn generate_evm_account_list(
26    account_infos: &[AccountInfo],
27    account_list_type: EvmAccountList,
28) -> PermissionedActionAllowedBy<EthereumAccountId> {
29    // The signer of pallet-evm transactions is the EVM domain in some tests, so we also add it to
30    // the account lists.
31    let evm_domain_account = hex!("e04cc55ebee1cbce552f250e85c57b70b2e2625b");
32
33    match account_list_type {
34        EvmAccountList::Anyone => PermissionedActionAllowedBy::Anyone,
35        EvmAccountList::NoOne => PermissionedActionAllowedBy::Accounts(Vec::new()),
36        EvmAccountList::One => PermissionedActionAllowedBy::Accounts(vec![
37            EthereumAccountId::from(evm_domain_account),
38            EthereumAccountId::from(account_infos[0].address),
39        ]),
40        EvmAccountList::Multiple => PermissionedActionAllowedBy::Accounts(vec![
41            EthereumAccountId::from(evm_domain_account),
42            EthereumAccountId::from(account_infos[0].address),
43            EthereumAccountId::from(account_infos[1].address),
44            EthereumAccountId::from(account_infos[2].address),
45        ]),
46    }
47}
48
49/// Generate a pallet-evm call, which can be passed to `construct_and_send_extrinsic()`.
50/// `use_create` determines whether to use `create`, `create2`, or a non-create call.
51/// `recursion_depth` determines the number of `pallet_utility::Call` wrappers to use.
52pub fn generate_evm_domain_call<TestRuntime>(
53    account_info: AccountInfo,
54    use_create: ethereum::TransactionAction,
55    recursion_depth: u8,
56    nonce: U256,
57    gas_price: U256,
58) -> RuntimeCallFor<TestRuntime>
59where
60    TestRuntime: frame_system::Config + pallet_evm::Config + pallet_utility::Config,
61    RuntimeCallFor<TestRuntime>:
62        From<pallet_utility::Call<TestRuntime>> + From<pallet_evm::Call<TestRuntime>>,
63{
64    if recursion_depth > 0 {
65        let inner_call = generate_evm_domain_call::<TestRuntime>(
66            account_info,
67            use_create,
68            recursion_depth - 1,
69            nonce,
70            gas_price,
71        );
72
73        // TODO:
74        // - randomly choose from the 6 different utility wrapper calls
75        // - test this call as the second call in a batch
76        // - test __Ignore calls are ignored
77        return pallet_utility::Call::<TestRuntime>::batch {
78            calls: vec![inner_call.into()],
79        }
80        .into();
81    }
82
83    let call = match use_create {
84        // TODO:
85        // - randomly choose from Create or Create2 calls
86        ethereum::TransactionAction::Create => pallet_evm::Call::<TestRuntime>::create {
87            source: account_info.address,
88            init: vec![0; 100],
89            value: U256::zero(),
90            gas_limit: max_extrinsic_gas::<TestRuntime>(1000),
91            max_fee_per_gas: gas_price,
92            access_list: vec![],
93            max_priority_fee_per_gas: Some(U256::from(1)),
94            nonce: Some(nonce),
95            authorization_list: vec![],
96        },
97        ethereum::TransactionAction::Call(contract) => pallet_evm::Call::<TestRuntime>::call {
98            source: account_info.address,
99            target: contract,
100            input: vec![0; 100],
101            value: U256::zero(),
102            gas_limit: max_extrinsic_gas::<TestRuntime>(1000),
103            max_fee_per_gas: gas_price,
104            max_priority_fee_per_gas: Some(U256::from(1)),
105            nonce: Some(nonce),
106            access_list: vec![],
107            authorization_list: vec![],
108        },
109    };
110
111    call.into()
112}
113
114pub fn max_extrinsic_gas<TestRuntime: frame_system::Config + pallet_evm::Config>(
115    multiplier: u64,
116) -> u64 {
117    let limits: frame_system::limits::BlockWeights =
118        <TestRuntime as frame_system::Config>::BlockWeights::get();
119    // `limits.get(DispatchClass::Normal).max_extrinsic` is too large to use as `gas_limit`
120    // thus use `base_extrinsic`
121    let max_extrinsic = limits.get(DispatchClass::Normal).base_extrinsic * multiplier;
122
123    <TestRuntime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(max_extrinsic)
124}
125
126pub fn generate_legacy_transfer_txn<TestRuntime: frame_system::Config + pallet_evm::Config>(
127    account_info: AccountInfo,
128    nonce: U256,
129    gas_price: U256,
130    to: H160,
131    value: U256,
132) -> Transaction {
133    LegacyUnsignedTransaction {
134        nonce,
135        gas_price,
136        gas_limit: U256::from(21000),
137        action: ethereum::TransactionAction::Call(to),
138        value,
139        input: vec![],
140    }
141    .sign(&account_info.private_key)
142    .into()
143}
144
145pub fn generate_legacy_tx<TestRuntime: frame_system::Config + pallet_evm::Config>(
146    account_info: AccountInfo,
147    nonce: U256,
148    action: ethereum::TransactionAction,
149    input: Vec<u8>,
150    gas_price: U256,
151) -> Transaction {
152    LegacyUnsignedTransaction {
153        nonce,
154        gas_price,
155        gas_limit: U256::from(max_extrinsic_gas::<TestRuntime>(1000)),
156        action,
157        value: U256::zero(),
158        input,
159    }
160    .sign(&account_info.private_key)
161    .into()
162}
163
164pub fn generate_eip2930_transfer_txn<TestRuntime: frame_system::Config + pallet_evm::Config>(
165    account_info: AccountInfo,
166    nonce: U256,
167    gas_price: U256,
168    to: H160,
169    value: U256,
170) -> Transaction {
171    EIP2930UnsignedTransaction {
172        nonce,
173        gas_price,
174        gas_limit: U256::from(21000),
175        action: ethereum::TransactionAction::Call(to),
176        value,
177        input: vec![],
178    }
179    .sign(&account_info.private_key, None)
180    .into()
181}
182
183pub fn generate_eip2930_tx<TestRuntime: frame_system::Config + pallet_evm::Config>(
184    account_info: AccountInfo,
185    nonce: U256,
186    action: ethereum::TransactionAction,
187    input: Vec<u8>,
188    gas_price: U256,
189) -> Transaction {
190    EIP2930UnsignedTransaction {
191        nonce,
192        gas_price,
193        gas_limit: U256::from(max_extrinsic_gas::<TestRuntime>(100)),
194        action,
195        value: U256::one(),
196        input,
197    }
198    .sign(&account_info.private_key, None)
199    .into()
200}
201
202pub fn generate_eip1559_transfer_txn<TestRuntime: frame_system::Config + pallet_evm::Config>(
203    account_info: AccountInfo,
204    nonce: U256,
205    gas_price: U256,
206    to: H160,
207    value: U256,
208) -> Transaction {
209    EIP1559UnsignedTransaction {
210        nonce,
211        max_priority_fee_per_gas: gas_price,
212        max_fee_per_gas: gas_price.saturating_mul(U256::from(2)),
213        gas_limit: U256::from(21000),
214        action: ethereum::TransactionAction::Call(to),
215        value,
216        input: vec![],
217    }
218    .sign(&account_info.private_key, None)
219    .into()
220}
221
222pub fn generate_eip1559_tx<TestRuntime: frame_system::Config + pallet_evm::Config>(
223    account_info: AccountInfo,
224    nonce: U256,
225    action: ethereum::TransactionAction,
226    input: Vec<u8>,
227    gas_price: U256,
228) -> Transaction {
229    EIP1559UnsignedTransaction {
230        nonce,
231        max_priority_fee_per_gas: U256::from(1),
232        max_fee_per_gas: gas_price,
233        gas_limit: U256::from(max_extrinsic_gas::<TestRuntime>(1000)),
234        action,
235        value: U256::zero(),
236        input,
237    }
238    .sign(&account_info.private_key, None)
239    .into()
240}