difftreelog
feat default sponsoring rate limit
in: master
2 files changed
pallets/contract-helpers/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23pub use pallet::*;45#[frame_support::pallet]6pub mod pallet {7 use frame_support::sp_runtime::traits::StaticLookup;8 use frame_support::{pallet_prelude::*, traits::IsSubType};9 use frame_system::pallet_prelude::*;10 use pallet_contracts::chain_extension::UncheckedFrom;11 use sp_runtime::{12 traits::{DispatchInfoOf, Hash, PostDispatchInfoOf, SignedExtension},13 transaction_validity,14 };15 use sp_std::vec::Vec;16 use up_sponsorship::SponsorshipHandler;1718 #[pallet::error]19 pub enum Error<T> {20 /// Should be contract owner21 NoPermission,22 }2324 #[pallet::config]25 pub trait Config: frame_system::Config + pallet_contracts::Config {}2627 #[pallet::pallet]28 #[pallet::generate_store(pub(super) trait Store)]29 pub struct Pallet<T>(_);3031 #[pallet::storage]32 pub(super) type Owner<T: Config> = StorageMap<33 Hasher = Twox128,34 Key = T::AccountId,35 Value = T::AccountId,36 QueryKind = ValueQuery,37 >;3839 #[pallet::storage]40 pub(super) type AllowlistEnabled<T: Config> =41 StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;4243 #[pallet::storage]44 pub(super) type Allowlist<T: Config> = StorageDoubleMap<45 Hasher1 = Twox128,46 Key1 = T::AccountId,47 Hasher2 = Twox64Concat,48 Key2 = T::AccountId,49 Value = bool,50 QueryKind = ValueQuery,51 >;5253 #[pallet::storage]54 pub(super) type SelfSponsoring<T: Config> =55 StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;5657 #[pallet::storage]58 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<59 Hasher = Twox128,60 Key = T::AccountId,61 Value = T::BlockNumber,62 QueryKind = ValueQuery,63 >;6465 #[pallet::storage]66 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<67 Hasher1 = Twox128,68 Key1 = T::AccountId,69 Hasher2 = Twox128,70 Key2 = T::AccountId,71 Value = T::BlockNumber,72 QueryKind = ValueQuery,73 >;7475 #[pallet::call]76 impl<T: Config> Pallet<T> {77 #[pallet::weight(0)]78 pub fn toggle_sponsoring(79 origin: OriginFor<T>,80 contract: T::AccountId,81 sponsoring: bool,82 ) -> DispatchResult {83 let sender = ensure_signed(origin)?;84 ensure!(85 <Owner<T>>::get(&contract) == sender,86 <Error<T>>::NoPermission87 );8889 if sponsoring {90 <SelfSponsoring<T>>::insert(contract, true);91 } else {92 <SelfSponsoring<T>>::remove(contract);93 }94 Ok(())95 }9697 #[pallet::weight(0)]98 pub fn toggle_allowlist(99 origin: OriginFor<T>,100 contract: T::AccountId,101 enabled: bool,102 ) -> DispatchResult {103 let sender = ensure_signed(origin)?;104 ensure!(105 <Owner<T>>::get(&contract) == sender,106 <Error<T>>::NoPermission107 );108109 if enabled {110 <AllowlistEnabled<T>>::insert(contract, true);111 } else {112 <AllowlistEnabled<T>>::remove(contract);113 }114 Ok(())115 }116117 #[pallet::weight(0)]118 pub fn toggle_allowed(119 origin: OriginFor<T>,120 contract: T::AccountId,121 user: T::AccountId,122 allowed: bool,123 ) -> DispatchResult {124 let sender = ensure_signed(origin)?;125 ensure!(126 <Owner<T>>::get(&contract) == sender,127 <Error<T>>::NoPermission128 );129130 if allowed {131 <Allowlist<T>>::insert(contract, user, true);132 } else {133 <Allowlist<T>>::remove(contract, user);134 }135 Ok(())136 }137138 #[pallet::weight(0)]139 pub fn set_sponsoring_rate_limit(140 origin: OriginFor<T>,141 contract: T::AccountId,142 rate_limit: T::BlockNumber,143 ) -> DispatchResult {144 let sender = ensure_signed(origin)?;145 ensure!(146 <Owner<T>>::get(&contract) == sender,147 <Error<T>>::NoPermission148 );149150 <SponsoringRateLimit<T>>::insert(contract, rate_limit);151 Ok(())152 }153 }154155 #[derive(Encode, Decode, Clone, PartialEq, Eq)]156 pub struct ContractHelpersExtension<T>(PhantomData<T>);157 impl<T> core::fmt::Debug for ContractHelpersExtension<T> {158 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {159 fmt.debug_struct("ContractHelpersExtension").finish()160 }161 }162163 type CodeHash<T> = <T as frame_system::Config>::Hash;164 impl<T> SignedExtension for ContractHelpersExtension<T>165 where166 T: Config + Send + Sync,167 T::Call: sp_runtime::traits::Dispatchable,168 T::Call: IsSubType<pallet_contracts::Call<T>>,169 T::AccountId: UncheckedFrom<T::Hash>,170 T::AccountId: AsRef<[u8]>,171 {172 const IDENTIFIER: &'static str = "ContractHelpers";173 type AccountId = T::AccountId;174 type Call = T::Call;175 type AdditionalSigned = ();176 type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;177178 fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {179 Ok(())180 }181182 fn validate(183 &self,184 who: &T::AccountId,185 call: &Self::Call,186 _info: &DispatchInfoOf<Self::Call>,187 _len: usize,188 ) -> transaction_validity::TransactionValidity {189 if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =190 IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)191 {192 let called_contract: T::AccountId =193 T::Lookup::lookup((*dest).clone()).unwrap_or_default();194 if <AllowlistEnabled<T>>::get(&called_contract)195 && !<Allowlist<T>>::get(&called_contract, who)196 && &<Owner<T>>::get(&called_contract) != who197 {198 return Err(transaction_validity::InvalidTransaction::Call.into());199 }200 }201 Ok(transaction_validity::ValidTransaction::default())202 }203204 fn pre_dispatch(205 self,206 who: &Self::AccountId,207 call: &Self::Call,208 _info: &DispatchInfoOf<Self::Call>,209 _len: usize,210 ) -> Result<Self::Pre, TransactionValidityError> {211 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {212 Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {213 Ok(Some((who.clone(), *code_hash, salt.clone())))214 }215 Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {216 let code_hash = &T::Hashing::hash(code);217 Ok(Some((who.clone(), *code_hash, salt.clone())))218 }219 _ => Ok(None),220 }221 }222223 fn post_dispatch(224 pre: Self::Pre,225 _info: &DispatchInfoOf<Self::Call>,226 _post_info: &PostDispatchInfoOf<Self::Call>,227 _len: usize,228 _result: &DispatchResult,229 ) -> Result<(), TransactionValidityError> {230 if let Some((who, code_hash, salt)) = pre {231 let new_contract_address =232 <pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);233 <Owner<T>>::insert(&new_contract_address, &who);234 }235236 Ok(())237 }238 }239240 pub struct ContractSponsorshipHandler<T>(PhantomData<T>);241 impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>242 where243 T: Config,244 C: IsSubType<pallet_contracts::Call<T>>,245 T::AccountId: UncheckedFrom<T::Hash>,246 T::AccountId: AsRef<[u8]>,247 {248 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {249 if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =250 IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)251 {252 let called_contract: T::AccountId =253 T::Lookup::lookup((*dest).clone()).unwrap_or_default();254 if <SelfSponsoring<T>>::get(&called_contract) {255 let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);256 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;257 let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);258 let limit_time = last_tx_block + rate_limit;259260 if block_number >= limit_time {261 SponsorBasket::<T>::insert(&called_contract, who, block_number);262 return Some(called_contract);263 }264 }265 }266 None267 }268 }269}1#![cfg_attr(not(feature = "std"), no_std)]23pub use pallet::*;45#[frame_support::pallet]6pub mod pallet {7 use frame_support::sp_runtime::traits::StaticLookup;8 use frame_support::{pallet_prelude::*, traits::IsSubType};9 use frame_system::pallet_prelude::*;10 use pallet_contracts::chain_extension::UncheckedFrom;11 use sp_runtime::{12 traits::{DispatchInfoOf, Hash, PostDispatchInfoOf, SignedExtension},13 transaction_validity,14 };15 use sp_std::vec::Vec;16 use up_sponsorship::SponsorshipHandler;1718 #[pallet::error]19 pub enum Error<T> {20 /// Should be contract owner21 NoPermission,22 }2324 #[pallet::config]25 pub trait Config: frame_system::Config + pallet_contracts::Config {26 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;27 }2829 #[pallet::pallet]30 #[pallet::generate_store(pub(super) trait Store)]31 pub struct Pallet<T>(_);3233 #[pallet::storage]34 pub(super) type Owner<T: Config> = StorageMap<35 Hasher = Twox128,36 Key = T::AccountId,37 Value = T::AccountId,38 QueryKind = ValueQuery,39 >;4041 #[pallet::storage]42 pub(super) type AllowlistEnabled<T: Config> =43 StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;4445 #[pallet::storage]46 pub(super) type Allowlist<T: Config> = StorageDoubleMap<47 Hasher1 = Twox128,48 Key1 = T::AccountId,49 Hasher2 = Twox64Concat,50 Key2 = T::AccountId,51 Value = bool,52 QueryKind = ValueQuery,53 >;5455 #[pallet::storage]56 pub(super) type SelfSponsoring<T: Config> =57 StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;5859 #[pallet::storage]60 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<61 Hasher = Twox128,62 Key = T::AccountId,63 Value = T::BlockNumber,64 QueryKind = ValueQuery,65 OnEmpty = T::DefaultSponsoringRateLimit,66 >;6768 #[pallet::storage]69 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<70 Hasher1 = Twox128,71 Key1 = T::AccountId,72 Hasher2 = Twox128,73 Key2 = T::AccountId,74 Value = T::BlockNumber,75 QueryKind = OptionQuery,76 >;7778 #[pallet::call]79 impl<T: Config> Pallet<T> {80 #[pallet::weight(0)]81 pub fn toggle_sponsoring(82 origin: OriginFor<T>,83 contract: T::AccountId,84 sponsoring: bool,85 ) -> DispatchResult {86 let sender = ensure_signed(origin)?;87 ensure!(88 <Owner<T>>::get(&contract) == sender,89 <Error<T>>::NoPermission90 );9192 if sponsoring {93 <SelfSponsoring<T>>::insert(contract, true);94 } else {95 <SelfSponsoring<T>>::remove(contract);96 }97 Ok(())98 }99100 #[pallet::weight(0)]101 pub fn toggle_allowlist(102 origin: OriginFor<T>,103 contract: T::AccountId,104 enabled: bool,105 ) -> DispatchResult {106 let sender = ensure_signed(origin)?;107 ensure!(108 <Owner<T>>::get(&contract) == sender,109 <Error<T>>::NoPermission110 );111112 if enabled {113 <AllowlistEnabled<T>>::insert(contract, true);114 } else {115 <AllowlistEnabled<T>>::remove(contract);116 }117 Ok(())118 }119120 #[pallet::weight(0)]121 pub fn toggle_allowed(122 origin: OriginFor<T>,123 contract: T::AccountId,124 user: T::AccountId,125 allowed: bool,126 ) -> DispatchResult {127 let sender = ensure_signed(origin)?;128 ensure!(129 <Owner<T>>::get(&contract) == sender,130 <Error<T>>::NoPermission131 );132133 if allowed {134 <Allowlist<T>>::insert(contract, user, true);135 } else {136 <Allowlist<T>>::remove(contract, user);137 }138 Ok(())139 }140141 #[pallet::weight(0)]142 pub fn set_sponsoring_rate_limit(143 origin: OriginFor<T>,144 contract: T::AccountId,145 rate_limit: T::BlockNumber,146 ) -> DispatchResult {147 let sender = ensure_signed(origin)?;148 ensure!(149 <Owner<T>>::get(&contract) == sender,150 <Error<T>>::NoPermission151 );152153 <SponsoringRateLimit<T>>::insert(contract, rate_limit);154 Ok(())155 }156 }157158 #[derive(Encode, Decode, Clone, PartialEq, Eq)]159 pub struct ContractHelpersExtension<T>(PhantomData<T>);160 impl<T> core::fmt::Debug for ContractHelpersExtension<T> {161 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {162 fmt.debug_struct("ContractHelpersExtension").finish()163 }164 }165166 type CodeHash<T> = <T as frame_system::Config>::Hash;167 impl<T> SignedExtension for ContractHelpersExtension<T>168 where169 T: Config + Send + Sync,170 T::Call: sp_runtime::traits::Dispatchable,171 T::Call: IsSubType<pallet_contracts::Call<T>>,172 T::AccountId: UncheckedFrom<T::Hash>,173 T::AccountId: AsRef<[u8]>,174 {175 const IDENTIFIER: &'static str = "ContractHelpers";176 type AccountId = T::AccountId;177 type Call = T::Call;178 type AdditionalSigned = ();179 type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;180181 fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {182 Ok(())183 }184185 fn validate(186 &self,187 who: &T::AccountId,188 call: &Self::Call,189 _info: &DispatchInfoOf<Self::Call>,190 _len: usize,191 ) -> transaction_validity::TransactionValidity {192 if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =193 IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)194 {195 let called_contract: T::AccountId =196 T::Lookup::lookup((*dest).clone()).unwrap_or_default();197 if <AllowlistEnabled<T>>::get(&called_contract)198 && !<Allowlist<T>>::get(&called_contract, who)199 && &<Owner<T>>::get(&called_contract) != who200 {201 return Err(transaction_validity::InvalidTransaction::Call.into());202 }203 }204 Ok(transaction_validity::ValidTransaction::default())205 }206207 fn pre_dispatch(208 self,209 who: &Self::AccountId,210 call: &Self::Call,211 _info: &DispatchInfoOf<Self::Call>,212 _len: usize,213 ) -> Result<Self::Pre, TransactionValidityError> {214 match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {215 Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {216 Ok(Some((who.clone(), *code_hash, salt.clone())))217 }218 Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {219 let code_hash = &T::Hashing::hash(code);220 Ok(Some((who.clone(), *code_hash, salt.clone())))221 }222 _ => Ok(None),223 }224 }225226 fn post_dispatch(227 pre: Self::Pre,228 _info: &DispatchInfoOf<Self::Call>,229 _post_info: &PostDispatchInfoOf<Self::Call>,230 _len: usize,231 _result: &DispatchResult,232 ) -> Result<(), TransactionValidityError> {233 if let Some((who, code_hash, salt)) = pre {234 let new_contract_address =235 <pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);236 <Owner<T>>::insert(&new_contract_address, &who);237 }238239 Ok(())240 }241 }242243 pub struct ContractSponsorshipHandler<T>(PhantomData<T>);244 impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>245 where246 T: Config,247 C: IsSubType<pallet_contracts::Call<T>>,248 T::AccountId: UncheckedFrom<T::Hash>,249 T::AccountId: AsRef<[u8]>,250 {251 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {252 if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =253 IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)254 {255 let called_contract: T::AccountId =256 T::Lookup::lookup((*dest).clone()).unwrap_or_default();257 if <SelfSponsoring<T>>::get(&called_contract) {258 let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);259 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;260 let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);261 let limit_time = last_tx_block + rate_limit;262263 if block_number >= limit_time {264 SponsorBasket::<T>::insert(&called_contract, who, block_number);265 return Some(called_contract);266 }267 }268 }269 None270 }271 }272}pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -13,6 +13,7 @@
#[pallet::config]
pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config {
type ContractAddress: Get<H160>;
+ type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
}
#[pallet::error]
@@ -34,8 +35,13 @@
StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;
#[pallet::storage]
- pub(super) type SponsoringRateLimit<T: Config> =
- StorageMap<Hasher = Twox128, Key = H160, Value = T::BlockNumber, QueryKind = ValueQuery>;
+ pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
+ Hasher = Twox128,
+ Key = H160,
+ Value = T::BlockNumber,
+ QueryKind = ValueQuery,
+ OnEmpty = T::DefaultSponsoringRateLimit,
+ >;
#[pallet::storage]
pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<