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
23pub mod check_nonce;
24pub mod create_contract;
25pub mod fees;
26pub mod traits;
27
28pub use check_nonce::CheckNonce;
29use domain_runtime_primitives::EthereumAccountId;
30pub use pallet::*;
31use sp_core::U256;
32use sp_domains::PermissionedActionAllowedBy;
33
34#[frame_support::pallet]
35mod pallet {
36    use domain_runtime_primitives::EthereumAccountId;
37    use frame_support::pallet_prelude::*;
38    use frame_system::pallet_prelude::*;
39    use sp_core::U256;
40    use sp_domains::PermissionedActionAllowedBy;
41    use sp_evm_tracker::{InherentError, InherentType, INHERENT_IDENTIFIER};
42
43    #[pallet::config]
44    pub trait Config: frame_system::Config {}
45
46    /// Storage to hold evm account nonces.
47    /// This is only used for pre_dispatch since EVM pre_dispatch does not
48    /// increment account nonce.
49    #[pallet::storage]
50    pub(super) type AccountNonce<T: Config> =
51        StorageMap<_, Identity, T::AccountId, U256, OptionQuery>;
52
53    /// Storage to hold EVM contract creation allow list accounts.
54    /// Unlike domain instantiation, no storage value means "anyone can create contracts".
55    ///
56    /// At genesis, this is set via DomainConfigParams, DomainRuntimeInfo::Evm, and
57    /// RawGenesis::set_evm_contract_creation_allowed_by().
58    // When this type name is changed, evm_contract_creation_allowed_by_storage_key() also needs to
59    // be updated.
60    #[pallet::storage]
61    pub(super) type ContractCreationAllowedBy<T: Config> = StorageValue<
62        _,
63        PermissionedActionAllowedBy<EthereumAccountId>,
64        ValueQuery,
65        DefaultToAnyone,
66    >;
67
68    /// Default value for ContractCreationAllowedBy if it is not set.
69    pub struct DefaultToAnyone;
70
71    impl Get<PermissionedActionAllowedBy<EthereumAccountId>> for DefaultToAnyone {
72        fn get() -> PermissionedActionAllowedBy<EthereumAccountId> {
73            PermissionedActionAllowedBy::Anyone
74        }
75    }
76
77    /// Pallet EVM account nonce tracker.
78    #[pallet::pallet]
79    #[pallet::without_storage_info]
80    pub struct Pallet<T>(_);
81
82    #[pallet::call]
83    impl<T: Config> Pallet<T> {
84        /// An inherent call to set ContractCreationAllowedBy.
85        #[pallet::call_index(0)]
86        #[pallet::weight((T::DbWeight::get().reads_writes(0, 1), DispatchClass::Mandatory))]
87        pub fn set_contract_creation_allowed_by(
88            origin: OriginFor<T>,
89            contract_creation_allowed_by: PermissionedActionAllowedBy<EthereumAccountId>,
90        ) -> DispatchResult {
91            ensure_none(origin)?;
92
93            // signer and is_private_evm_domain() were already checked by pallet-domains.
94
95            ContractCreationAllowedBy::<T>::put(contract_creation_allowed_by);
96
97            Ok(())
98        }
99    }
100
101    #[pallet::inherent]
102    impl<T: Config> ProvideInherent for Pallet<T> {
103        type Call = Call<T>;
104        type Error = InherentError;
105        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
106
107        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
108            let inherent_data = data
109                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
110                .expect("EVM tracker inherent data not correctly encoded")
111                .expect("EVM tracker inherent data must be provided");
112
113            inherent_data
114                .maybe_call
115                .map(
116                    |contract_creation_allowed_by| Call::set_contract_creation_allowed_by {
117                        contract_creation_allowed_by,
118                    },
119                )
120        }
121
122        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
123            let inherent_data = data
124                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
125                .expect("EVM tracker inherent data not correctly encoded")
126                .expect("EVM tracker inherent data must be provided");
127
128            Ok(inherent_data
129                .maybe_call
130                .map(|_encoded_call| InherentError::MissingRuntimeCall))
131        }
132
133        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
134            let maybe_provided_call = Self::create_inherent(data);
135
136            if let Some(provided_call) = maybe_provided_call {
137                if Self::is_inherent(call) && call != &provided_call {
138                    return Err(InherentError::IncorrectRuntimeCall);
139                }
140            } else {
141                return Err(InherentError::MissingRuntimeCall);
142            }
143
144            Ok(())
145        }
146
147        fn is_inherent(call: &Self::Call) -> bool {
148            matches!(call, Call::set_contract_creation_allowed_by { .. })
149        }
150    }
151
152    #[pallet::hooks]
153    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
154        fn on_finalize(_now: BlockNumberFor<T>) {
155            // clear the nonce storage, since we would start with updated nonce
156            // during the pre_dispatch in next block
157            let _ = AccountNonce::<T>::clear(u32::MAX, None);
158        }
159    }
160}
161
162impl<T: Config> Pallet<T> {
163    /// Returns current nonce for the given account.
164    pub fn account_nonce(account: T::AccountId) -> Option<U256> {
165        AccountNonce::<T>::get(account)
166    }
167
168    /// Set nonce to the account.
169    pub fn set_account_nonce(account: T::AccountId, nonce: U256) {
170        AccountNonce::<T>::set(account, Some(nonce))
171    }
172
173    /// Returns true if the supplied account is allowed to create contracts.
174    pub fn is_allowed_to_create_contracts(signer: &EthereumAccountId) -> bool {
175        ContractCreationAllowedBy::<T>::get().is_allowed(signer)
176    }
177
178    /// Returns true if any account is allowed to create contracts.
179    pub fn is_allowed_to_create_unsigned_contracts() -> bool {
180        ContractCreationAllowedBy::<T>::get().is_anyone_allowed()
181    }
182
183    /// Returns the current contract creation allow list.
184    /// Mainly used in tests.
185    pub fn contract_creation_allowed_by() -> PermissionedActionAllowedBy<EthereumAccountId> {
186        ContractCreationAllowedBy::<T>::get()
187    }
188}