difftreelog
docs
in: master
2 files changed
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};19use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};20use pallet_evm::{21 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,22 account::CrossAccountId,23};24use sp_core::H160;25use up_data_structs::SponsorshipState;26use crate::{27 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT, Sponsoring,28};29use frame_support::traits::Get;30use up_sponsorship::SponsorshipHandler;31use sp_std::vec::Vec;3233struct ContractHelpers<T: Config>(SubstrateRecorder<T>);34impl<T: Config> WithRecorder<T> for ContractHelpers<T> {35 fn recorder(&self) -> &SubstrateRecorder<T> {36 &self.037 }3839 fn into_recorder(self) -> SubstrateRecorder<T> {40 self.041 }42}4344#[solidity_interface(name = "ContractHelpers")]45impl<T: Config> ContractHelpers<T>46where47 T::AccountId: AsRef<[u8]>,48{49 fn contract_owner(&self, contract_address: address) -> Result<address> {50 Ok(<Owner<T>>::get(contract_address))51 }5253 fn set_sponsor(54 &mut self,55 caller: caller,56 contract_address: address,57 sponsor: address,58 ) -> Result<void> {59 Pallet::<T>::set_sponsor(60 &T::CrossAccountId::from_eth(caller),61 contract_address,62 &T::CrossAccountId::from_eth(sponsor),63 )64 .map_err(dispatch_to_evm::<T>)?;65 Ok(())66 }6768 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {69 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)70 .map_err(dispatch_to_evm::<T>)?;71 Ok(())72 }7374 fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {75 let sponsor =76 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;77 let sponsor_sub = pallet_common::eth::convert_cross_account_to_eth_uint256::<T>(&sponsor);78 Ok((*sponsor.as_eth(), sponsor_sub))79 }8081 fn has_sponsor(&self, contract_address: address) -> Result<bool> {82 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())83 }8485 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {86 Ok(match Sponsoring::<T>::get(contract_address) {87 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,88 SponsorshipState::Unconfirmed(_) => true,89 })90 }9192 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {93 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)94 }9596 fn set_sponsoring_mode(97 &mut self,98 caller: caller,99 contract_address: address,100 mode: uint8,101 ) -> Result<void> {102 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;103 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;104 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);105 Ok(())106 }107108 fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {109 Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())110 }111112 fn set_sponsoring_rate_limit(113 &mut self,114 caller: caller,115 contract_address: address,116 rate_limit: uint32,117 ) -> Result<void> {118 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;119 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());120 Ok(())121 }122123 fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {124 Ok(<SponsoringRateLimit<T>>::get(contract_address)125 .try_into()126 .map_err(|_| "rate limit > u32::MAX")?)127 }128129 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {130 self.0.consume_sload()?;131 Ok(<Pallet<T>>::allowed(contract_address, user))132 }133134 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {135 Ok(<AllowlistEnabled<T>>::get(contract_address))136 }137138 fn toggle_allowlist(139 &mut self,140 caller: caller,141 contract_address: address,142 enabled: bool,143 ) -> Result<void> {144 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;145 <Pallet<T>>::toggle_allowlist(contract_address, enabled);146 Ok(())147 }148149 fn toggle_allowed(150 &mut self,151 caller: caller,152 contract_address: address,153 user: address,154 allowed: bool,155 ) -> Result<void> {156 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;157 <Pallet<T>>::toggle_allowed(contract_address, user, allowed);158 Ok(())159 }160}161162pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);163impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>164where165 T::AccountId: AsRef<[u8]>,166{167 fn is_reserved(contract: &sp_core::H160) -> bool {168 contract == &T::ContractAddress::get()169 }170171 fn is_used(contract: &sp_core::H160) -> bool {172 contract == &T::ContractAddress::get()173 }174175 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {176 // TODO: Extract to another OnMethodCall handler177 if <AllowlistEnabled<T>>::get(handle.code_address())178 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)179 {180 return Some(Err(PrecompileFailure::Revert {181 exit_status: ExitRevert::Reverted,182 output: {183 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));184 writer.string("Target contract is allowlisted");185 writer.finish()186 },187 }));188 }189190 if handle.code_address() != T::ContractAddress::get() {191 return None;192 }193194 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));195 pallet_evm_coder_substrate::call(handle, helpers)196 }197198 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {199 (contract == &T::ContractAddress::get())200 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())201 }202}203204pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);205impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {206 fn on_create(owner: H160, contract: H160) {207 <Owner<T>>::insert(contract, owner);208 }209}210211pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);212impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>213 for HelpersContractSponsoring<T>214{215 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {216 let (contract, _) = call;217 let mode = <Pallet<T>>::sponsoring_mode(*contract);218 if mode == SponsoringModeT::Disabled {219 return None;220 }221222 let sponsor = match <Pallet<T>>::get_sponsor(*contract) {223 Some(sponsor) => sponsor,224 None => return None,225 };226227 if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(*contract, *who.as_eth()) {228 return None;229 }230 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;231232 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract, who.as_eth()) {233 let limit = <SponsoringRateLimit<T>>::get(contract);234235 let timeout = last_tx_block + limit;236 if block_number < timeout {237 return None;238 }239 }240241 <SponsorBasket<T>>::insert(contract, who.as_eth(), block_number);242243 Some(sponsor)244 }245}246247generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);248generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};19use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};20use pallet_evm::{21 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,22 account::CrossAccountId,23};24use sp_core::H160;25use up_data_structs::SponsorshipState;26use crate::{27 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT, Sponsoring,28};29use frame_support::traits::Get;30use up_sponsorship::SponsorshipHandler;31use sp_std::vec::Vec;3233struct ContractHelpers<T: Config>(SubstrateRecorder<T>);34impl<T: Config> WithRecorder<T> for ContractHelpers<T> {35 fn recorder(&self) -> &SubstrateRecorder<T> {36 &self.037 }3839 fn into_recorder(self) -> SubstrateRecorder<T> {40 self.041 }42}4344#[solidity_interface(name = "ContractHelpers")]45impl<T: Config> ContractHelpers<T>46where47 T::AccountId: AsRef<[u8]>,48{49 /// Get contract ovner50 /// 51 /// @param Contract_address contract for which the owner is being determined.52 /// @return Contract owner.53 fn contract_owner(&self, contract_address: address) -> Result<address> {54 Ok(<Owner<T>>::get(contract_address))55 }5657 /// Set sponsor.58 /// 59 /// @param contract_address Contract for which a sponsor is being established.60 /// @param sponsor User address who set as pending sponsor.61 fn set_sponsor(62 &mut self,63 caller: caller,64 contract_address: address,65 sponsor: address,66 ) -> Result<void> {67 Pallet::<T>::set_sponsor(68 &T::CrossAccountId::from_eth(caller),69 contract_address,70 &T::CrossAccountId::from_eth(sponsor),71 )72 .map_err(dispatch_to_evm::<T>)?;73 Ok(())74 }7576 /// Confirm sponsorship.77 /// 78 /// @dev Caller must be same that set via [`set_sponsor`].79 /// 80 /// @param contract_address Сontract for which need to confirm sponsorship.81 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {82 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)83 .map_err(dispatch_to_evm::<T>)?;84 Ok(())85 }8687 /// Get current sponsor.88 /// 89 /// @param contract_address The contract for which a sponsor is requested.90 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.91 fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {92 let sponsor =93 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;94 let sponsor_sub = pallet_common::eth::convert_cross_account_to_eth_uint256::<T>(&sponsor);95 Ok((*sponsor.as_eth(), sponsor_sub))96 }9798 /// Check tat contract has confirmed sponsor.99 /// 100 /// @param contract_address The contract for which the presence of a confirmed sponsor is checked.101 /// @return **true** if contract has confirmed sponsor.102 fn has_sponsor(&self, contract_address: address) -> Result<bool> {103 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())104 }105106 /// Check tat contract has pending sponsor.107 /// 108 /// @param contract_address The contract for which the presence of a pending sponsor is checked.109 /// @return **true** if contract has pending sponsor.110 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {111 Ok(match Sponsoring::<T>::get(contract_address) {112 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,113 SponsorshipState::Unconfirmed(_) => true,114 })115 }116117 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {118 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)119 }120121 fn set_sponsoring_mode(122 &mut self,123 caller: caller,124 contract_address: address,125 mode: uint8,126 ) -> Result<void> {127 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;128 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;129 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);130 Ok(())131 }132133 fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {134 Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())135 }136137 fn set_sponsoring_rate_limit(138 &mut self,139 caller: caller,140 contract_address: address,141 rate_limit: uint32,142 ) -> Result<void> {143 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;144 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());145 Ok(())146 }147148 fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {149 Ok(<SponsoringRateLimit<T>>::get(contract_address)150 .try_into()151 .map_err(|_| "rate limit > u32::MAX")?)152 }153154 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {155 self.0.consume_sload()?;156 Ok(<Pallet<T>>::allowed(contract_address, user))157 }158159 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {160 Ok(<AllowlistEnabled<T>>::get(contract_address))161 }162163 fn toggle_allowlist(164 &mut self,165 caller: caller,166 contract_address: address,167 enabled: bool,168 ) -> Result<void> {169 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;170 <Pallet<T>>::toggle_allowlist(contract_address, enabled);171 Ok(())172 }173174 fn toggle_allowed(175 &mut self,176 caller: caller,177 contract_address: address,178 user: address,179 allowed: bool,180 ) -> Result<void> {181 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;182 <Pallet<T>>::toggle_allowed(contract_address, user, allowed);183 Ok(())184 }185}186187pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);188impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>189where190 T::AccountId: AsRef<[u8]>,191{192 fn is_reserved(contract: &sp_core::H160) -> bool {193 contract == &T::ContractAddress::get()194 }195196 fn is_used(contract: &sp_core::H160) -> bool {197 contract == &T::ContractAddress::get()198 }199200 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {201 // TODO: Extract to another OnMethodCall handler202 if <AllowlistEnabled<T>>::get(handle.code_address())203 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)204 {205 return Some(Err(PrecompileFailure::Revert {206 exit_status: ExitRevert::Reverted,207 output: {208 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));209 writer.string("Target contract is allowlisted");210 writer.finish()211 },212 }));213 }214215 if handle.code_address() != T::ContractAddress::get() {216 return None;217 }218219 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));220 pallet_evm_coder_substrate::call(handle, helpers)221 }222223 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {224 (contract == &T::ContractAddress::get())225 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())226 }227}228229pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);230impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {231 fn on_create(owner: H160, contract: H160) {232 <Owner<T>>::insert(contract, owner);233 }234}235236pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);237impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>238 for HelpersContractSponsoring<T>239{240 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {241 let (contract, _) = call;242 let mode = <Pallet<T>>::sponsoring_mode(*contract);243 if mode == SponsoringModeT::Disabled {244 return None;245 }246247 let sponsor = match <Pallet<T>>::get_sponsor(*contract) {248 Some(sponsor) => sponsor,249 None => return None,250 };251252 if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(*contract, *who.as_eth()) {253 return None;254 }255 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;256257 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract, who.as_eth()) {258 let limit = <SponsoringRateLimit<T>>::get(contract);259260 let timeout = last_tx_block + limit;261 if block_number < timeout {262 return None;263 }264 }265266 <SponsorBasket<T>>::insert(contract, who.as_eth(), block_number);267268 Some(sponsor)269 }270}271272generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);273generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -101,6 +101,11 @@
OnEmpty = T::DefaultSponsoringRateLimit,
>;
+ /// Storage for last sponsored block.
+ ///
+ /// * **Key1** - contract address.
+ /// * **Key2** - sponsored user address.
+ /// * **Value** - last sponsored block number.
#[pallet::storage]
pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<
Hasher1 = Twox128,
@@ -151,6 +156,14 @@
}
impl<T: Config> Pallet<T> {
+ /// Get contract owner.
+ pub fn contract_owner(contract: H160) -> H160 {
+ <Owner<T>>::get(contract)
+ }
+
+ /// Set `sponsor` for `contract`.
+ ///
+ /// `sender` must be owner of contract.
pub fn set_sponsor(
sender: &T::CrossAccountId,
contract: H160,
@@ -164,6 +177,9 @@
Ok(())
}
+ /// Confirm sponsorship.
+ ///
+ /// `sender` must be same that set via [`set_sponsor`].
pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {
match Sponsoring::<T>::get(contract) {
SponsorshipState::Unconfirmed(sponsor) => {
@@ -181,6 +197,7 @@
}
}
+ /// Get sponsor.
pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {
match Sponsoring::<T>::get(contract) {
SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,
@@ -224,12 +241,6 @@
pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {
ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);
Ok(())
- }
- }
-
- impl<T: Config> Pallet<T> {
- pub fn contract_owner(contract: H160) -> H160 {
- <Owner<T>>::get(contract)
}
}
}