difftreelog
fmt
in: master
1 file 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,28 Sponsoring,29};30use frame_support::traits::Get;31use up_sponsorship::SponsorshipHandler;32use sp_std::vec::Vec;3334struct ContractHelpers<T: Config>(SubstrateRecorder<T>);35impl<T: Config> WithRecorder<T> for ContractHelpers<T> {36 fn recorder(&self) -> &SubstrateRecorder<T> {37 &self.038 }3940 fn into_recorder(self) -> SubstrateRecorder<T> {41 self.042 }43}4445#[solidity_interface(name = "ContractHelpers")]46impl<T: Config> ContractHelpers<T>47where48 T::AccountId: AsRef<[u8; 32]>,49{50 /// Get contract ovner51 ///52 /// @param Contract_address contract for which the owner is being determined.53 /// @return Contract owner.54 fn contract_owner(&self, contract_address: address) -> Result<address> {55 Ok(<Owner<T>>::get(contract_address))56 }5758 /// Set sponsor.59 ///60 /// @param contract_address Contract for which a sponsor is being established.61 /// @param sponsor User address who set as pending sponsor.62 fn set_sponsor(63 &mut self,64 caller: caller,65 contract_address: address,66 sponsor: address,67 ) -> Result<void> {68 self.recorder().consume_sload()?;69 self.recorder().consume_sstore()?;7071 Pallet::<T>::set_sponsor(72 &T::CrossAccountId::from_eth(caller),73 contract_address,74 &T::CrossAccountId::from_eth(sponsor),75 )76 .map_err(dispatch_to_evm::<T>)?;7778 Ok(())79 }8081 /// Set contract as self sponsored.82 ///83 /// @param contract_address Contract for which a self sponsoring is being enabled.84 fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {85 self.recorder().consume_sload()?;86 self.recorder().consume_sstore()?;8788 Pallet::<T>::self_sponsored_enable(&T::CrossAccountId::from_eth(caller), contract_address)89 .map_err(dispatch_to_evm::<T>)?;9091 Ok(())92 }9394 /// Remove sponsor.95 ///96 /// @param contract_address Contract for which a sponsorship is being removed.97 fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {98 self.recorder().consume_sload()?;99 self.recorder().consume_sstore()?;100101 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)102 .map_err(dispatch_to_evm::<T>)?;103104 Ok(())105 }106107 /// Confirm sponsorship.108 ///109 /// @dev Caller must be same that set via [`set_sponsor`].110 ///111 /// @param contract_address Сontract for which need to confirm sponsorship.112 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {113 self.recorder().consume_sload()?;114 self.recorder().consume_sstore()?;115116 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)117 .map_err(dispatch_to_evm::<T>)?;118119 Ok(())120 }121122 /// Get current sponsor.123 ///124 /// @param contract_address The contract for which a sponsor is requested.125 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.126 fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {127 let sponsor =128 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;129 let result: (address, uint256) = if sponsor.is_canonical_substrate() {130 let sponsor = pallet_common::eth::convert_cross_account_to_uint256::<T>(&sponsor);131 (Default::default(), sponsor)132 } else {133 let sponsor = *sponsor.as_eth();134 (sponsor, Default::default())135 };136 Ok(result)137 }138139 /// Check tat contract has confirmed sponsor.140 ///141 /// @param contract_address The contract for which the presence of a confirmed sponsor is checked.142 /// @return **true** if contract has confirmed sponsor.143 fn has_sponsor(&self, contract_address: address) -> Result<bool> {144 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())145 }146147 /// Check tat contract has pending sponsor.148 ///149 /// @param contract_address The contract for which the presence of a pending sponsor is checked.150 /// @return **true** if contract has pending sponsor.151 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {152 Ok(match Sponsoring::<T>::get(contract_address) {153 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,154 SponsorshipState::Unconfirmed(_) => true,155 })156 }157158 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {159 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)160 }161162 fn set_sponsoring_mode(163 &mut self,164 caller: caller,165 contract_address: address,166 mode: uint8,167 ) -> Result<void> {168 self.recorder().consume_sload()?;169 self.recorder().consume_sstore()?;170171 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;172 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;173 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);174175 Ok(())176 }177178 fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {179 Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())180 }181182 fn set_sponsoring_rate_limit(183 &mut self,184 caller: caller,185 contract_address: address,186 rate_limit: uint32,187 ) -> Result<void> {188 self.recorder().consume_sload()?;189 self.recorder().consume_sstore()?;190191 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;192 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());193194 Ok(())195 }196197 fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {198 Ok(<SponsoringRateLimit<T>>::get(contract_address)199 .try_into()200 .map_err(|_| "rate limit > u32::MAX")?)201 }202203 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {204 self.0.consume_sload()?;205 Ok(<Pallet<T>>::allowed(contract_address, user))206 }207208 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {209 Ok(<AllowlistEnabled<T>>::get(contract_address))210 }211212 fn toggle_allowlist(213 &mut self,214 caller: caller,215 contract_address: address,216 enabled: bool,217 ) -> Result<void> {218 self.recorder().consume_sload()?;219 self.recorder().consume_sstore()?;220221 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;222 <Pallet<T>>::toggle_allowlist(contract_address, enabled);223224 Ok(())225 }226227 fn toggle_allowed(228 &mut self,229 caller: caller,230 contract_address: address,231 user: address,232 allowed: bool,233 ) -> Result<void> {234 self.recorder().consume_sload()?;235 self.recorder().consume_sstore()?;236 237 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;238 <Pallet<T>>::toggle_allowed(contract_address, user, allowed);239240 Ok(())241 }242}243244pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);245impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>246where247 T::AccountId: AsRef<[u8; 32]>,248{249 fn is_reserved(contract: &sp_core::H160) -> bool {250 contract == &T::ContractAddress::get()251 }252253 fn is_used(contract: &sp_core::H160) -> bool {254 contract == &T::ContractAddress::get()255 }256257 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {258 // TODO: Extract to another OnMethodCall handler259 if <AllowlistEnabled<T>>::get(handle.code_address())260 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)261 {262 return Some(Err(PrecompileFailure::Revert {263 exit_status: ExitRevert::Reverted,264 output: {265 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));266 writer.string("Target contract is allowlisted");267 writer.finish()268 },269 }));270 }271272 if handle.code_address() != T::ContractAddress::get() {273 return None;274 }275276 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));277 pallet_evm_coder_substrate::call(handle, helpers)278 }279280 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {281 (contract == &T::ContractAddress::get())282 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())283 }284}285286pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);287impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {288 fn on_create(owner: H160, contract: H160) {289 <Owner<T>>::insert(contract, owner);290 }291}292293pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);294impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>295 for HelpersContractSponsoring<T>296{297 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {298 let (contract_address, _) = call;299 let mode = <Pallet<T>>::sponsoring_mode(*contract_address);300 if mode == SponsoringModeT::Disabled {301 return None;302 }303304 let sponsor = match <Pallet<T>>::get_sponsor(*contract_address) {305 Some(sponsor) => sponsor,306 None => return None,307 };308309 if mode == SponsoringModeT::Allowlisted310 && !<Pallet<T>>::allowed(*contract_address, *who.as_eth())311 {312 return None;313 }314 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;315316 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {317 let limit = <SponsoringRateLimit<T>>::get(contract_address);318319 let timeout = last_tx_block + limit;320 if block_number < timeout {321 return None;322 }323 }324325 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);326327 Some(sponsor)328 }329}330331generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);332generate_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,28 Sponsoring,29};30use frame_support::traits::Get;31use up_sponsorship::SponsorshipHandler;32use sp_std::vec::Vec;3334struct ContractHelpers<T: Config>(SubstrateRecorder<T>);35impl<T: Config> WithRecorder<T> for ContractHelpers<T> {36 fn recorder(&self) -> &SubstrateRecorder<T> {37 &self.038 }3940 fn into_recorder(self) -> SubstrateRecorder<T> {41 self.042 }43}4445#[solidity_interface(name = "ContractHelpers")]46impl<T: Config> ContractHelpers<T>47where48 T::AccountId: AsRef<[u8; 32]>,49{50 /// Get contract ovner51 ///52 /// @param Contract_address contract for which the owner is being determined.53 /// @return Contract owner.54 fn contract_owner(&self, contract_address: address) -> Result<address> {55 Ok(<Owner<T>>::get(contract_address))56 }5758 /// Set sponsor.59 ///60 /// @param contract_address Contract for which a sponsor is being established.61 /// @param sponsor User address who set as pending sponsor.62 fn set_sponsor(63 &mut self,64 caller: caller,65 contract_address: address,66 sponsor: address,67 ) -> Result<void> {68 self.recorder().consume_sload()?;69 self.recorder().consume_sstore()?;7071 Pallet::<T>::set_sponsor(72 &T::CrossAccountId::from_eth(caller),73 contract_address,74 &T::CrossAccountId::from_eth(sponsor),75 )76 .map_err(dispatch_to_evm::<T>)?;7778 Ok(())79 }8081 /// Set contract as self sponsored.82 ///83 /// @param contract_address Contract for which a self sponsoring is being enabled.84 fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {85 self.recorder().consume_sload()?;86 self.recorder().consume_sstore()?;8788 Pallet::<T>::self_sponsored_enable(&T::CrossAccountId::from_eth(caller), contract_address)89 .map_err(dispatch_to_evm::<T>)?;9091 Ok(())92 }9394 /// Remove sponsor.95 ///96 /// @param contract_address Contract for which a sponsorship is being removed.97 fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {98 self.recorder().consume_sload()?;99 self.recorder().consume_sstore()?;100101 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)102 .map_err(dispatch_to_evm::<T>)?;103104 Ok(())105 }106107 /// Confirm sponsorship.108 ///109 /// @dev Caller must be same that set via [`set_sponsor`].110 ///111 /// @param contract_address Сontract for which need to confirm sponsorship.112 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {113 self.recorder().consume_sload()?;114 self.recorder().consume_sstore()?;115116 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)117 .map_err(dispatch_to_evm::<T>)?;118119 Ok(())120 }121122 /// Get current sponsor.123 ///124 /// @param contract_address The contract for which a sponsor is requested.125 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.126 fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {127 let sponsor =128 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;129 let result: (address, uint256) = if sponsor.is_canonical_substrate() {130 let sponsor = pallet_common::eth::convert_cross_account_to_uint256::<T>(&sponsor);131 (Default::default(), sponsor)132 } else {133 let sponsor = *sponsor.as_eth();134 (sponsor, Default::default())135 };136 Ok(result)137 }138139 /// Check tat contract has confirmed sponsor.140 ///141 /// @param contract_address The contract for which the presence of a confirmed sponsor is checked.142 /// @return **true** if contract has confirmed sponsor.143 fn has_sponsor(&self, contract_address: address) -> Result<bool> {144 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())145 }146147 /// Check tat contract has pending sponsor.148 ///149 /// @param contract_address The contract for which the presence of a pending sponsor is checked.150 /// @return **true** if contract has pending sponsor.151 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {152 Ok(match Sponsoring::<T>::get(contract_address) {153 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,154 SponsorshipState::Unconfirmed(_) => true,155 })156 }157158 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {159 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)160 }161162 fn set_sponsoring_mode(163 &mut self,164 caller: caller,165 contract_address: address,166 mode: uint8,167 ) -> Result<void> {168 self.recorder().consume_sload()?;169 self.recorder().consume_sstore()?;170171 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;172 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;173 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);174175 Ok(())176 }177178 fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {179 Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())180 }181182 fn set_sponsoring_rate_limit(183 &mut self,184 caller: caller,185 contract_address: address,186 rate_limit: uint32,187 ) -> Result<void> {188 self.recorder().consume_sload()?;189 self.recorder().consume_sstore()?;190191 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;192 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());193194 Ok(())195 }196197 fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {198 Ok(<SponsoringRateLimit<T>>::get(contract_address)199 .try_into()200 .map_err(|_| "rate limit > u32::MAX")?)201 }202203 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {204 self.0.consume_sload()?;205 Ok(<Pallet<T>>::allowed(contract_address, user))206 }207208 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {209 Ok(<AllowlistEnabled<T>>::get(contract_address))210 }211212 fn toggle_allowlist(213 &mut self,214 caller: caller,215 contract_address: address,216 enabled: bool,217 ) -> Result<void> {218 self.recorder().consume_sload()?;219 self.recorder().consume_sstore()?;220221 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;222 <Pallet<T>>::toggle_allowlist(contract_address, enabled);223224 Ok(())225 }226227 fn toggle_allowed(228 &mut self,229 caller: caller,230 contract_address: address,231 user: address,232 allowed: bool,233 ) -> Result<void> {234 self.recorder().consume_sload()?;235 self.recorder().consume_sstore()?;236237 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;238 <Pallet<T>>::toggle_allowed(contract_address, user, allowed);239240 Ok(())241 }242}243244pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);245impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>246where247 T::AccountId: AsRef<[u8; 32]>,248{249 fn is_reserved(contract: &sp_core::H160) -> bool {250 contract == &T::ContractAddress::get()251 }252253 fn is_used(contract: &sp_core::H160) -> bool {254 contract == &T::ContractAddress::get()255 }256257 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {258 // TODO: Extract to another OnMethodCall handler259 if <AllowlistEnabled<T>>::get(handle.code_address())260 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)261 {262 return Some(Err(PrecompileFailure::Revert {263 exit_status: ExitRevert::Reverted,264 output: {265 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));266 writer.string("Target contract is allowlisted");267 writer.finish()268 },269 }));270 }271272 if handle.code_address() != T::ContractAddress::get() {273 return None;274 }275276 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));277 pallet_evm_coder_substrate::call(handle, helpers)278 }279280 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {281 (contract == &T::ContractAddress::get())282 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())283 }284}285286pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);287impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {288 fn on_create(owner: H160, contract: H160) {289 <Owner<T>>::insert(contract, owner);290 }291}292293pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);294impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>295 for HelpersContractSponsoring<T>296{297 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {298 let (contract_address, _) = call;299 let mode = <Pallet<T>>::sponsoring_mode(*contract_address);300 if mode == SponsoringModeT::Disabled {301 return None;302 }303304 let sponsor = match <Pallet<T>>::get_sponsor(*contract_address) {305 Some(sponsor) => sponsor,306 None => return None,307 };308309 if mode == SponsoringModeT::Allowlisted310 && !<Pallet<T>>::allowed(*contract_address, *who.as_eth())311 {312 return None;313 }314 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;315316 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {317 let limit = <SponsoringRateLimit<T>>::get(contract_address);318319 let timeout = last_tx_block + limit;320 if block_number < timeout {321 return None;322 }323 }324325 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);326327 Some(sponsor)328 }329}330331generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);332generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);