pallet_evm_tracker/
lib.rs

1// Copyright (C) 2023 Subspace Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Pallet EVM Account nonce tracker
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20#[cfg(not(feature = "std"))]
21extern crate alloc;
22
23#[cfg(feature = "runtime-benchmarks")]
24mod benchmarking;
25
26pub mod check_nonce;
27pub mod create_contract;
28pub mod fees;
29pub mod traits;
30pub mod weights;
31
32pub use check_nonce::CheckNonce;
33use domain_runtime_primitives::EthereumAccountId;
34pub use pallet::*;
35use sp_core::U256;
36use sp_domains::PermissionedActionAllowedBy;
37use sp_weights::Weight;
38
39/// Maximum number of calls we benchmarked for.
40const MAXIMUM_NUMBER_OF_CALLS: u32 = 5_000;
41
42/// Weight functions needed for pallet_evm_tracker.
43pub trait WeightInfo {
44    fn evm_contract_check_multiple(c: u32) -> Weight;
45    fn evm_contract_check_nested(c: u32) -> Weight;
46}
47
48#[frame_support::pallet]
49mod pallet {
50    use domain_runtime_primitives::EthereumAccountId;
51    use frame_support::pallet_prelude::*;
52    use frame_system::pallet_prelude::*;
53    use sp_core::U256;
54    use sp_domains::PermissionedActionAllowedBy;
55    use sp_evm_tracker::{INHERENT_IDENTIFIER, InherentError, InherentType};
56
57    #[pallet::config]
58    pub trait Config: frame_system::Config {}
59
60    /// Storage to hold evm account nonces.
61    /// This is only used for pre_dispatch since EVM pre_dispatch does not
62    /// increment account nonce.
63    #[pallet::storage]
64    pub(super) type AccountNonce<T: Config> =
65        StorageMap<_, Identity, T::AccountId, U256, OptionQuery>;
66
67    /// Storage to hold EVM contract creation allow list accounts.
68    /// Unlike domain instantiation, no storage value means "anyone can create contracts".
69    ///
70    /// At genesis, this is set via DomainConfigParams, DomainRuntimeInfo::Evm, and
71    /// RawGenesis::set_evm_contract_creation_allowed_by().
72    // When this type name is changed, evm_contract_creation_allowed_by_storage_key() also needs to
73    // be updated.
74    #[pallet::storage]
75    pub(super) type ContractCreationAllowedBy<T: Config> = StorageValue<
76        _,
77        PermissionedActionAllowedBy<EthereumAccountId>,
78        ValueQuery,
79        DefaultToAnyone,
80    >;
81
82    /// Default value for ContractCreationAllowedBy if it is not set.
83    pub struct DefaultToAnyone;
84
85    impl Get<PermissionedActionAllowedBy<EthereumAccountId>> for DefaultToAnyone {
86        fn get() -> PermissionedActionAllowedBy<EthereumAccountId> {
87            PermissionedActionAllowedBy::Anyone
88        }
89    }
90
91    /// Pallet EVM account nonce tracker.
92    #[pallet::pallet]
93    #[pallet::without_storage_info]
94    pub struct Pallet<T>(_);
95
96    #[pallet::call]
97    impl<T: Config> Pallet<T> {
98        /// An inherent call to set ContractCreationAllowedBy.
99        #[pallet::call_index(0)]
100        #[pallet::weight((T::DbWeight::get().reads_writes(0, 1), DispatchClass::Mandatory))]
101        pub fn set_contract_creation_allowed_by(
102            origin: OriginFor<T>,
103            contract_creation_allowed_by: PermissionedActionAllowedBy<EthereumAccountId>,
104        ) -> DispatchResult {
105            ensure_none(origin)?;
106
107            // signer and is_private_evm_domain() were already checked by pallet-domains.
108
109            ContractCreationAllowedBy::<T>::put(contract_creation_allowed_by);
110
111            Ok(())
112        }
113    }
114
115    #[pallet::inherent]
116    impl<T: Config> ProvideInherent for Pallet<T> {
117        type Call = Call<T>;
118        type Error = InherentError;
119        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
120
121        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
122            let inherent_data = data
123                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
124                .expect("EVM tracker inherent data not correctly encoded")
125                .expect("EVM tracker inherent data must be provided");
126
127            inherent_data
128                .maybe_call
129                .map(
130                    |contract_creation_allowed_by| Call::set_contract_creation_allowed_by {
131                        contract_creation_allowed_by,
132                    },
133                )
134        }
135
136        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
137            let inherent_data = data
138                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
139                .expect("EVM tracker inherent data not correctly encoded")
140                .expect("EVM tracker inherent data must be provided");
141
142            Ok(inherent_data
143                .maybe_call
144                .map(|_encoded_call| InherentError::MissingRuntimeCall))
145        }
146
147        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
148            let maybe_provided_call = Self::create_inherent(data);
149
150            if let Some(provided_call) = maybe_provided_call {
151                if Self::is_inherent(call) && call != &provided_call {
152                    return Err(InherentError::IncorrectRuntimeCall);
153                }
154            } else {
155                return Err(InherentError::MissingRuntimeCall);
156            }
157
158            Ok(())
159        }
160
161        fn is_inherent(call: &Self::Call) -> bool {
162            matches!(call, Call::set_contract_creation_allowed_by { .. })
163        }
164    }
165
166    #[pallet::hooks]
167    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
168        fn on_finalize(_now: BlockNumberFor<T>) {
169            // clear the nonce storage, since we would start with updated nonce
170            // during the pre_dispatch in next block
171            let _ = AccountNonce::<T>::clear(u32::MAX, None);
172        }
173    }
174}
175
176impl<T: Config> Pallet<T> {
177    /// Returns current nonce for the given account.
178    pub fn account_nonce(account: T::AccountId) -> Option<U256> {
179        AccountNonce::<T>::get(account)
180    }
181
182    /// Set nonce to the account.
183    pub fn set_account_nonce(account: T::AccountId, nonce: U256) {
184        AccountNonce::<T>::set(account, Some(nonce))
185    }
186
187    /// Returns true if the supplied account is allowed to create contracts.
188    pub fn is_allowed_to_create_contracts(signer: &EthereumAccountId) -> bool {
189        ContractCreationAllowedBy::<T>::get().is_allowed(signer)
190    }
191
192    /// Returns true if any account is allowed to create contracts.
193    pub fn is_allowed_to_create_unsigned_contracts() -> bool {
194        ContractCreationAllowedBy::<T>::get().is_anyone_allowed()
195    }
196
197    /// Returns the current contract creation allow list.
198    /// Mainly used in tests.
199    pub fn contract_creation_allowed_by() -> PermissionedActionAllowedBy<EthereumAccountId> {
200        ContractCreationAllowedBy::<T>::get()
201    }
202}