git.delta.rocks / unique-network / refs/commits / ea3a682dbe54

difftreelog

fix only enable sponsoring if rate limit is on

Yaroslav Bolyukin2021-07-28parent: #8a7066a.patch.diff
in: master

1 file changed

modifiedpallets/contract-helpers/src/lib.rsdiffbeforeafterboth
before · pallets/contract-helpers/src/lib.rs
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}
after · pallets/contract-helpers/src/lib.rs
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 = ValueQuery,76	>;7778	impl<T: Config> Pallet<T> {79		pub fn allowed(contract: T::AccountId, user: T::AccountId, default: bool) -> bool {80			if !<AllowlistEnabled<T>>::get(&contract) {81				return default;82			}83			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(contract) == user84		}85	}8687	#[pallet::call]88	impl<T: Config> Pallet<T> {89		#[pallet::weight(0)]90		pub fn toggle_sponsoring(91			origin: OriginFor<T>,92			contract: T::AccountId,93			sponsoring: bool,94		) -> DispatchResult {95			let sender = ensure_signed(origin)?;96			ensure!(97				<Owner<T>>::get(&contract) == sender,98				<Error<T>>::NoPermission99			);100101			if sponsoring {102				<SelfSponsoring<T>>::insert(contract, true);103			} else {104				<SelfSponsoring<T>>::remove(contract);105			}106			Ok(())107		}108109		#[pallet::weight(0)]110		pub fn toggle_allowlist(111			origin: OriginFor<T>,112			contract: T::AccountId,113			enabled: bool,114		) -> DispatchResult {115			let sender = ensure_signed(origin)?;116			ensure!(117				<Owner<T>>::get(&contract) == sender,118				<Error<T>>::NoPermission119			);120121			if enabled {122				<AllowlistEnabled<T>>::insert(contract, true);123			} else {124				<AllowlistEnabled<T>>::remove(contract);125			}126			Ok(())127		}128129		#[pallet::weight(0)]130		pub fn toggle_allowed(131			origin: OriginFor<T>,132			contract: T::AccountId,133			user: T::AccountId,134			allowed: bool,135		) -> DispatchResult {136			let sender = ensure_signed(origin)?;137			ensure!(138				<Owner<T>>::get(&contract) == sender,139				<Error<T>>::NoPermission140			);141142			if allowed {143				<Allowlist<T>>::insert(contract, user, true);144			} else {145				<Allowlist<T>>::remove(contract, user);146			}147			Ok(())148		}149150		#[pallet::weight(0)]151		pub fn set_sponsoring_rate_limit(152			origin: OriginFor<T>,153			contract: T::AccountId,154			rate_limit: T::BlockNumber,155		) -> DispatchResult {156			let sender = ensure_signed(origin)?;157			ensure!(158				<Owner<T>>::get(&contract) == sender,159				<Error<T>>::NoPermission160			);161162			<SponsoringRateLimit<T>>::insert(contract, rate_limit);163			Ok(())164		}165	}166167	#[derive(Encode, Decode, Clone, PartialEq, Eq)]168	pub struct ContractHelpersExtension<T>(PhantomData<T>);169	impl<T> core::fmt::Debug for ContractHelpersExtension<T> {170		fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {171			fmt.debug_struct("ContractHelpersExtension").finish()172		}173	}174175	type CodeHash<T> = <T as frame_system::Config>::Hash;176	impl<T> SignedExtension for ContractHelpersExtension<T>177	where178		T: Config + Send + Sync,179		T::Call: sp_runtime::traits::Dispatchable,180		T::Call: IsSubType<pallet_contracts::Call<T>>,181		T::AccountId: UncheckedFrom<T::Hash>,182		T::AccountId: AsRef<[u8]>,183	{184		const IDENTIFIER: &'static str = "ContractHelpers";185		type AccountId = T::AccountId;186		type Call = T::Call;187		type AdditionalSigned = ();188		type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;189190		fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {191			Ok(())192		}193194		fn validate(195			&self,196			who: &T::AccountId,197			call: &Self::Call,198			_info: &DispatchInfoOf<Self::Call>,199			_len: usize,200		) -> transaction_validity::TransactionValidity {201			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =202				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)203			{204				let called_contract: T::AccountId =205					T::Lookup::lookup((*dest).clone()).unwrap_or_default();206				if !<Pallet<T>>::allowed(called_contract, who.clone(), true) {207					return Err(transaction_validity::InvalidTransaction::Call.into());208				}209			}210			Ok(transaction_validity::ValidTransaction::default())211		}212213		fn pre_dispatch(214			self,215			who: &Self::AccountId,216			call: &Self::Call,217			_info: &DispatchInfoOf<Self::Call>,218			_len: usize,219		) -> Result<Self::Pre, TransactionValidityError> {220			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {221				Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {222					Ok(Some((who.clone(), *code_hash, salt.clone())))223				}224				Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {225					let code_hash = &T::Hashing::hash(code);226					Ok(Some((who.clone(), *code_hash, salt.clone())))227				}228				_ => Ok(None),229			}230		}231232		fn post_dispatch(233			pre: Self::Pre,234			_info: &DispatchInfoOf<Self::Call>,235			_post_info: &PostDispatchInfoOf<Self::Call>,236			_len: usize,237			_result: &DispatchResult,238		) -> Result<(), TransactionValidityError> {239			if let Some((who, code_hash, salt)) = pre {240				let new_contract_address =241					<pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);242				<Owner<T>>::insert(&new_contract_address, &who);243			}244245			Ok(())246		}247	}248249	pub struct ContractSponsorshipHandler<T>(PhantomData<T>);250	impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>251	where252		T: Config,253		C: IsSubType<pallet_contracts::Call<T>>,254		T::AccountId: UncheckedFrom<T::Hash>,255		T::AccountId: AsRef<[u8]>,256	{257		fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {258			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =259				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)260			{261				let called_contract: T::AccountId =262					T::Lookup::lookup((*dest).clone()).unwrap_or_default();263				if <SelfSponsoring<T>>::get(&called_contract)264					&& <Pallet<T>>::allowed(called_contract.clone(), who.clone(), false)265				{266					let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);267					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;268					let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);269					let limit_time = last_tx_block + rate_limit;270271					if block_number >= limit_time {272						SponsorBasket::<T>::insert(&called_contract, who, block_number);273						return Some(called_contract);274					}275				}276			}277			None278		}279	}280}