difftreelog
fix PR comments
in: master
8 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -7,6 +7,8 @@
use up_data_structs::TokenId;
pub struct CommonWeights<T: Config>(PhantomData<T>);
+
+// All implementations with `Weight::default` used in methods that return error `UnsupportedOperation`.
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items(_amount: &[up_data_structs::CreateItemData]) -> Weight {
Weight::default()
pallets/balances-adapter/src/erc.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/erc.rs
+++ b/pallets/balances-adapter/src/erc.rs
@@ -1,9 +1,8 @@
use crate::{Config, NativeFungibleHandle, Pallet, SelfWeightOf};
-use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};
-use frame_support::traits::{Currency, ExistenceRequirement};
+use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*};
+use frame_support::traits::{Currency};
use pallet_balances::WeightInfo;
use pallet_common::{
- consume_store_reads,
erc::{CommonEvmHandler, CrossAccountId, PrecompileHandle, PrecompileResult},
eth::CrossAddress,
};
@@ -14,38 +13,24 @@
};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
use sp_core::{U256, Get};
-use sp_std::vec::Vec;
frontier_contract! {
macro_rules! NativeFungibleHandle_result {...}
impl<T: Config> Contract for NativeFungibleHandle<T> {...}
}
-#[derive(ToLog)]
-pub enum ERC20Events {
- Transfer {
- #[indexed]
- from: Address,
- #[indexed]
- to: Address,
- value: U256,
- },
-}
-
-#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]
+#[solidity_interface(name = ERC20, enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]
impl<T: Config> NativeFungibleHandle<T> {
fn allowance(&self, _owner: Address, _spender: Address) -> Result<U256> {
Ok(U256::zero())
}
- // #[weight(<SelfWeightOf<T>>::approve())]
fn approve(&mut self, _caller: Caller, _spender: Address, _amount: U256) -> Result<bool> {
- // self.consume_store_reads(1)?;
Err("Approve not supported".into())
}
fn balance_of(&self, owner: Address) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <T as Config>::Currency::free_balance(owner.as_sub());
Ok(balance.into())
@@ -64,7 +49,7 @@
}
fn total_supply(&self) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let total = <T as Config>::Currency::total_issuance();
Ok(total.into())
}
@@ -111,7 +96,7 @@
T::AccountId: From<[u8; 32]>,
{
fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let owner = owner.into_sub_cross_account::<T>()?;
let balance = <T as Config>::Currency::free_balance(owner.as_sub());
Ok(balance.into())
@@ -122,18 +107,13 @@
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
let amount = amount.try_into().map_err(|_| "amount overflow")?;
- // let budget = self
- // .recorder
- // .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ let budget = self
+ .recorder()
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer(self, &caller, &to, amount, &budget)
+ .map_err(|e| dispatch_to_evm::<T>(e.error))?;
- // <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;
- <T as Config>::Currency::transfer(
- caller.as_sub(),
- to.as_sub(),
- amount,
- ExistenceRequirement::KeepAlive,
- )
- .map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
@@ -154,19 +134,13 @@
return Err("no permission".into());
}
- // let budget = self
- // .recorder
- // .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ let budget = self
+ .recorder()
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)
+ .map_err(|e| dispatch_to_evm::<T>(e.error))?;
- // <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)
- // .map_err(dispatch_to_evm::<T>)?;
- <T as Config>::Currency::transfer(
- caller.as_sub(),
- to.as_sub(),
- amount,
- ExistenceRequirement::KeepAlive,
- )
- .map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
}
pallets/balances-adapter/src/lib.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/lib.rs
+++ b/pallets/balances-adapter/src/lib.rs
@@ -1,4 +1,3 @@
-// #![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
@@ -12,9 +11,6 @@
pub mod erc;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
-
-const NATIVE_FUNGIBLE_COLLECTION_ID: up_data_structs::CollectionId =
- up_data_structs::CollectionId(0);
/// Handle for native fungible collection
pub struct NativeFungibleHandle<T: Config>(SubstrateRecorder<T>);
@@ -57,7 +53,10 @@
traits::{Currency, ExistenceRequirement, Get},
};
use pallet_balances::WeightInfo;
- use pallet_common::{erc::CrossAccountId, Error as CommonError, Pallet as PalletCommon};
+ use pallet_common::{
+ erc::CrossAccountId, Error as CommonError, Pallet as PalletCommon,
+ NATIVE_FUNGIBLE_COLLECTION_ID,
+ };
use pallet_structure::Pallet as PalletStructure;
use sp_core::U256;
use sp_runtime::DispatchError;
@@ -95,10 +94,9 @@
/// Checks if a non-owner has (enough) allowance from the owner to perform operations on the tokens.
/// Returns the expected remaining allowance - it should be set manually if the transaction proceeds.
///
- /// - `collection`: Collection that contains the token.
/// - `spender`: CrossAccountId who has the allowance rights.
/// - `from`: The owner of the tokens who sets the allowance.
- /// - `amount`: Amount of tokens by which the allowance sholud be reduced.
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
fn check_allowed(
spender: &T::CrossAccountId,
from: &T::CrossAccountId,
@@ -127,10 +125,11 @@
/// Transfers the specified amount of tokens. Will check that
/// the transfer is allowed for the token.
///
+ /// - `collection`: Collection that contains the token.
/// - `from`: Owner of tokens to transfer.
/// - `to`: Recepient of transfered tokens.
/// - `amount`: Amount of tokens to transfer.
- /// - `collection`: Collection that contains the token
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
pub fn transfer(
_collection: &NativeFungibleHandle<T>,
from: &T::CrossAccountId,
@@ -147,7 +146,7 @@
amount
.try_into()
.map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
- ExistenceRequirement::KeepAlive,
+ ExistenceRequirement::AllowDeath,
)?;
<PalletStructure<T>>::nest_if_sent_to_token(
@@ -175,6 +174,17 @@
})
}
+ /// Transfer NFT token from one account to another.
+ ///
+ /// Same as the [`Self::transfer`] but spender doesn't needs to be the owner of the token.
+ /// The owner should set allowance for the spender to transfer token.
+ ///
+ /// - `collection`: Collection that contains the token.
+ /// - `spender`: Account that spend the money.
+ /// - `from`: Owner of tokens to transfer.
+ /// - `to`: Recepient of transfered tokens.
+ /// - `amount`: Amount of tokens to transfer.
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
pub fn transfer_from(
collection: &NativeFungibleHandle<T>,
spender: &T::CrossAccountId,
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -77,14 +77,6 @@
fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;
}
-impl CommonEvmHandler for () {
- const CODE: &'static [u8] = &[];
-
- fn call(self, _handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
- None
- }
-}
-
/// @title A contract that allows you to work with collections.
#[solidity_interface(name = Collection, enum(derive(PreDispatch)), enum_attr(weight))]
impl<T: Config> CollectionHandle<T>
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -68,7 +68,6 @@
dispatch::Pays,
transactional, fail,
};
-use pallet_evm::GasWeightMapping;
use up_data_structs::{
AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,
RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,
@@ -101,7 +100,7 @@
/// Collection handle contains information about collection data and id.
/// Also provides functionality to count consumed gas.
///
-/// CollectionHandle is used as a generic wrapper for collections of all types.
+/// CollectionHandle is used as a generic wrapper for collections of all types (except native fungible).
/// It allows to perform common operations and queries on any collection type,
/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].
#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
@@ -153,7 +152,7 @@
&self,
reads: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_reads(self.recorder(), reads)
+ self.recorder().consume_store_reads(reads)
}
/// Consume gas for writing.
@@ -161,7 +160,7 @@
&self,
writes: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_writes(self.recorder(), writes)
+ self.recorder().consume_store_writes(writes)
}
/// Consume gas for reading and writing.
@@ -170,7 +169,8 @@
reads: u64,
writes: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_reads_and_writes(self.recorder(), reads, writes)
+ self.recorder()
+ .consume_store_reads_and_writes(reads, writes)
}
/// Save collection to storage.
@@ -441,7 +441,7 @@
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
/// Collection id for native fungible collction.
- pub const NATIVE_FINGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);
+ pub const NATIVE_FUNGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
@@ -2322,48 +2322,4 @@
PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,
}
}
-}
-
-/// Consume gas for reading.
-pub fn consume_store_reads<T: Config>(
- recorder: &SubstrateRecorder<T>,
- reads: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- <T as frame_system::Config>::DbWeight::get()
- .read
- .saturating_mul(reads),
- // TODO: measure proof
- 0,
- )))
-}
-
-/// Consume gas for writing.
-pub fn consume_store_writes<T: Config>(
- recorder: &SubstrateRecorder<T>,
- writes: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- <T as frame_system::Config>::DbWeight::get()
- .write
- .saturating_mul(writes),
- // TODO: measure proof
- 0,
- )))
-}
-
-/// Consume gas for reading and writing.
-pub fn consume_store_reads_and_writes<T: Config>(
- recorder: &SubstrateRecorder<T>,
- reads: u64,
- writes: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- let weight = <T as frame_system::Config>::DbWeight::get();
- let reads = weight.read.saturating_mul(reads);
- let writes = weight.read.saturating_mul(writes);
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- reads.saturating_add(writes),
- // TODO: measure proof
- 0,
- )))
}
pallets/evm-coder-substrate/src/lib.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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819extern crate self as pallet_evm_coder_substrate;2021#[cfg(not(feature = "std"))]22extern crate alloc;23#[cfg(not(feature = "std"))]24use alloc::format;25use execution::PreDispatch;26use frame_support::dispatch::Weight;2728use core::marker::PhantomData;29use sp_std::cell::RefCell;3031use codec::Decode;32use frame_support::pallet_prelude::DispatchError;33use frame_support::traits::PalletInfo;34use frame_support::{ensure, sp_runtime::ModuleError};35use up_data_structs::budget;36use pallet_evm::{37 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,38 PrecompileResult, PrecompileHandle,39};40use sp_core::H160;41// #[cfg(feature = "runtime-benchmarks")]42// pub mod benchmarking;43pub mod execution;44pub use evm_coder::*;4546#[doc(hidden)]47pub use spez::spez;4849use evm_coder::{50 abi::{AbiReader, AbiWrite, AbiWriter},51 types::{Msg, Value},52};5354pub use pallet::*;5556#[frame_support::pallet]57pub mod pallet {58 use super::*;5960 use frame_system::ensure_signed;61 pub use frame_support::dispatch::DispatchResult;62 use frame_system::pallet_prelude::*;6364 /// DispatchError is opaque, but we need to somehow extract correct error in case of OutOfGas failure65 /// So we have this pallet, which defines OutOfGas error, and knews its own id to check if DispatchError66 /// is thrown because of it67 ///68 /// These errors shouldn't end in extrinsic results, as they only used in evm execution path69 #[pallet::error]70 pub enum Error<T> {71 OutOfGas,72 OutOfFund,73 }7475 #[pallet::config]76 pub trait Config: frame_system::Config + pallet_evm::Config {}7778 #[pallet::pallet]79 pub struct Pallet<T>(_);8081 #[pallet::call]82 impl<T: Config> Pallet<T> {83 #[pallet::call_index(0)]84 #[pallet::weight(0)]85 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {86 let _sender = ensure_signed(origin)?;87 Ok(())88 }89 }90}9192// From instabul hardfork configuration: https://github.com/rust-blockchain/evm/blob/fd4fd6acc0ca3208d6770fdb3ba407c94cdf97c6/runtime/src/lib.rs#L28493pub const G_SLOAD_WORD: u64 = 800;94pub const G_SSTORE_WORD: u64 = 20000;9596pub struct GasCallsBudget<'r, T: Config> {97 recorder: &'r SubstrateRecorder<T>,98 gas_per_call: u64,99}100impl<T: Config> budget::Budget for GasCallsBudget<'_, T> {101 fn consume_custom(&self, calls: u32) -> bool {102 let (gas, overflown) = (calls as u64).overflowing_add(self.gas_per_call);103 if overflown {104 return false;105 }106 self.recorder.consume_gas(gas).is_ok()107 }108}109110#[derive(Default)]111pub struct SubstrateRecorder<T: Config> {112 initial_gas: u64,113 gas_limit: RefCell<u64>,114 _phantom: PhantomData<*const T>,115}116117impl<T: Config> SubstrateRecorder<T> {118 pub fn new(gas_limit: u64) -> Self {119 Self {120 initial_gas: gas_limit,121 gas_limit: RefCell::new(gas_limit),122 _phantom: PhantomData,123 }124 }125126 pub fn gas_left(&self) -> u64 {127 *self.gas_limit.borrow()128 }129 pub fn gas_calls_budget(&self, gas_per_call: u64) -> GasCallsBudget<T> {130 GasCallsBudget {131 recorder: self,132 gas_per_call,133 }134 }135 pub fn weight_calls_budget(&self, weight_per_call: Weight) -> GasCallsBudget<T> {136 GasCallsBudget {137 recorder: self,138 gas_per_call: T::GasWeightMapping::weight_to_gas(weight_per_call),139 }140 }141 pub fn consume_sload_sub(&self) -> DispatchResult {142 self.consume_gas_sub(G_SLOAD_WORD)143 }144 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {145 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))146 }147 pub fn consume_sstore_sub(&self) -> DispatchResult {148 self.consume_gas_sub(G_SSTORE_WORD)149 }150 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {151 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);152 let mut gas_limit = self.gas_limit.borrow_mut();153 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);154 *gas_limit -= gas;155 Ok(())156 }157158 pub fn consume_sload(&self) -> execution::Result<()> {159 self.consume_gas(G_SLOAD_WORD)160 }161 pub fn consume_sstore(&self) -> execution::Result<()> {162 self.consume_gas(G_SSTORE_WORD)163 }164 pub fn consume_gas(&self, gas: u64) -> execution::Result<()> {165 if gas == u64::MAX {166 return Err(execution::Error::Error(ExitError::OutOfGas));167 }168 let mut gas_limit = self.gas_limit.borrow_mut();169 if gas > *gas_limit {170 return Err(execution::Error::Error(ExitError::OutOfGas));171 }172 *gas_limit -= gas;173 Ok(())174 }175 pub fn return_gas(&self, gas: u64) {176 let mut gas_limit = self.gas_limit.borrow_mut();177 *gas_limit += gas;178 }179180 pub fn evm_to_precompile_output(181 self,182 handle: &mut impl PrecompileHandle,183 result: execution::Result<Option<AbiWriter>>,184 ) -> Option<PrecompileResult> {185 use execution::Error;186 // We ignore error here, as it should not occur, as we have our own bookkeeping of gas187 let _ = handle.record_cost(self.initial_gas - self.gas_left());188 Some(match result {189 Ok(Some(v)) => Ok(PrecompileOutput {190 exit_status: ExitSucceed::Returned,191 output: v.finish(),192 }),193 Ok(None) => return None,194 Err(Error::Revert(e)) => {195 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));196 (&e as &str).abi_write(&mut writer);197198 Err(PrecompileFailure::Revert {199 exit_status: ExitRevert::Reverted,200 output: writer.finish(),201 })202 }203 Err(Error::Fatal(f)) => Err(PrecompileFailure::Fatal { exit_status: f }),204 Err(Error::Error(e)) => Err(e.into()),205 })206 }207}208209pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> execution::Error {210 use execution::Error as ExError;211 match err {212 DispatchError::Module(ModuleError { index, error, .. })213 if index214 == T::PalletInfo::index::<Pallet<T>>()215 .expect("evm-coder-substrate is a pallet, which should be added to runtime")216 as u8 =>217 {218 let mut read = &error as &[u8];219 match Error::<T>::decode(&mut read) {220 Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),221 Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),222 _ => unreachable!("this pallet only defines two possible errors"),223 }224 }225 DispatchError::Module(ModuleError {226 message: Some(msg), ..227 }) => ExError::Revert(msg.into()),228 DispatchError::Module(ModuleError { index, error, .. }) => {229 ExError::Revert(format!("error {:?} in pallet {}", error, index))230 }231 e => ExError::Revert(format!("substrate error: {:?}", e)),232 }233}234235pub trait WithRecorder<T: Config> {236 fn recorder(&self) -> &SubstrateRecorder<T>;237 fn into_recorder(self) -> SubstrateRecorder<T>;238}239240/// Helper to simplify implementing bridge between evm-coder definitions and pallet-evm241pub fn call<T, C, E, H>(handle: &mut H, mut e: E) -> Option<PrecompileResult>242where243 T: Config,244 C: evm_coder::Call + PreDispatch,245 E: evm_coder::Callable<C> + WithRecorder<T>,246 H: PrecompileHandle,247 execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,248{249 let result = call_internal(250 handle.context().caller,251 &mut e,252 handle.context().apparent_value,253 handle.input(),254 );255 e.into_recorder().evm_to_precompile_output(handle, result)256}257258fn call_internal<T, C, E>(259 caller: H160,260 e: &mut E,261 value: Value,262 input: &[u8],263) -> execution::Result<Option<AbiWriter>>264where265 T: Config,266 C: evm_coder::Call + PreDispatch,267 E: Contract + evm_coder::Callable<C> + WithRecorder<T>,268 execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,269{270 let (selector, mut reader) = AbiReader::new_call(input)?;271 let call = C::parse(selector, &mut reader)?;272 if call.is_none() {273 let selector = u32::from_be_bytes(selector);274 return Err(format!("unrecognized selector: 0x{selector:0<8x}").into());275 }276 let call = call.unwrap();277278 let dispatch_info = call.dispatch_info();279 e.recorder()280 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;281282 match execution::ResultWithPostInfo::from(e.call(Msg {283 call,284 caller,285 value,286 })) {287 Ok(v) => {288 let unspent = v.post_info.calc_unspent(&dispatch_info);289 e.recorder()290 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));291 Ok(Some(v.data))292 }293 Err(v) => {294 let unspent = v.post_info.calc_unspent(&dispatch_info);295 e.recorder()296 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));297 Err(v.data)298 }299 }300}301302#[cfg(test)]303#[allow(dead_code)]304mod tests {305 use core::marker::PhantomData;306307 use evm_coder::ERC165Call;308 use frame_support::weights::Weight;309310 use crate::execution::PreDispatch;311312 #[derive(PreDispatch)]313 enum ExampleCall<T: super::Config> {314 ERC165Call(ERC165Call, PhantomData<fn() -> T>),315 OtherCall(ERC165Call),316317 #[weight(Weight::from_ref_time(a + b))]318 Example {319 a: u64,320 b: u64,321 },322 }323}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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819extern crate self as pallet_evm_coder_substrate;2021#[cfg(not(feature = "std"))]22extern crate alloc;23#[cfg(not(feature = "std"))]24use alloc::format;25use execution::PreDispatch;26use frame_support::dispatch::Weight;2728use core::marker::PhantomData;29use sp_std::cell::RefCell;3031use codec::Decode;32use frame_support::pallet_prelude::DispatchError;33use frame_support::traits::PalletInfo;34use frame_support::{ensure, sp_runtime::ModuleError};35use up_data_structs::budget;36use pallet_evm::{37 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,38 PrecompileResult, PrecompileHandle,39};40use sp_core::{Get, H160};41// #[cfg(feature = "runtime-benchmarks")]42// pub mod benchmarking;43pub mod execution;44pub use evm_coder::*;4546#[doc(hidden)]47pub use spez::spez;4849use evm_coder::{50 abi::{AbiReader, AbiWrite, AbiWriter},51 types::{Msg, Value},52};5354pub use pallet::*;5556#[frame_support::pallet]57pub mod pallet {58 use super::*;5960 use frame_system::ensure_signed;61 pub use frame_support::dispatch::DispatchResult;62 use frame_system::pallet_prelude::*;6364 /// DispatchError is opaque, but we need to somehow extract correct error in case of OutOfGas failure65 /// So we have this pallet, which defines OutOfGas error, and knews its own id to check if DispatchError66 /// is thrown because of it67 ///68 /// These errors shouldn't end in extrinsic results, as they only used in evm execution path69 #[pallet::error]70 pub enum Error<T> {71 OutOfGas,72 OutOfFund,73 }7475 #[pallet::config]76 pub trait Config: frame_system::Config + pallet_evm::Config {}7778 #[pallet::pallet]79 pub struct Pallet<T>(_);8081 #[pallet::call]82 impl<T: Config> Pallet<T> {83 #[pallet::call_index(0)]84 #[pallet::weight(0)]85 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {86 let _sender = ensure_signed(origin)?;87 Ok(())88 }89 }90}9192// From instabul hardfork configuration: https://github.com/rust-blockchain/evm/blob/fd4fd6acc0ca3208d6770fdb3ba407c94cdf97c6/runtime/src/lib.rs#L28493pub const G_SLOAD_WORD: u64 = 800;94pub const G_SSTORE_WORD: u64 = 20000;9596pub struct GasCallsBudget<'r, T: Config> {97 recorder: &'r SubstrateRecorder<T>,98 gas_per_call: u64,99}100impl<T: Config> budget::Budget for GasCallsBudget<'_, T> {101 fn consume_custom(&self, calls: u32) -> bool {102 let (gas, overflown) = (calls as u64).overflowing_add(self.gas_per_call);103 if overflown {104 return false;105 }106 self.recorder.consume_gas(gas).is_ok()107 }108}109110#[derive(Default)]111pub struct SubstrateRecorder<T: Config> {112 initial_gas: u64,113 gas_limit: RefCell<u64>,114 _phantom: PhantomData<*const T>,115}116117impl<T: Config> SubstrateRecorder<T> {118 pub fn new(gas_limit: u64) -> Self {119 Self {120 initial_gas: gas_limit,121 gas_limit: RefCell::new(gas_limit),122 _phantom: PhantomData,123 }124 }125126 pub fn gas_left(&self) -> u64 {127 *self.gas_limit.borrow()128 }129 pub fn gas_calls_budget(&self, gas_per_call: u64) -> GasCallsBudget<T> {130 GasCallsBudget {131 recorder: self,132 gas_per_call,133 }134 }135 pub fn weight_calls_budget(&self, weight_per_call: Weight) -> GasCallsBudget<T> {136 GasCallsBudget {137 recorder: self,138 gas_per_call: T::GasWeightMapping::weight_to_gas(weight_per_call),139 }140 }141 pub fn consume_sload_sub(&self) -> DispatchResult {142 self.consume_gas_sub(G_SLOAD_WORD)143 }144 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {145 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))146 }147 pub fn consume_sstore_sub(&self) -> DispatchResult {148 self.consume_gas_sub(G_SSTORE_WORD)149 }150 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {151 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);152 let mut gas_limit = self.gas_limit.borrow_mut();153 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);154 *gas_limit -= gas;155 Ok(())156 }157158 pub fn consume_sload(&self) -> execution::Result<()> {159 self.consume_gas(G_SLOAD_WORD)160 }161 pub fn consume_sstore(&self) -> execution::Result<()> {162 self.consume_gas(G_SSTORE_WORD)163 }164 pub fn consume_gas(&self, gas: u64) -> execution::Result<()> {165 if gas == u64::MAX {166 return Err(execution::Error::Error(ExitError::OutOfGas));167 }168 let mut gas_limit = self.gas_limit.borrow_mut();169 if gas > *gas_limit {170 return Err(execution::Error::Error(ExitError::OutOfGas));171 }172 *gas_limit -= gas;173 Ok(())174 }175 pub fn return_gas(&self, gas: u64) {176 let mut gas_limit = self.gas_limit.borrow_mut();177 *gas_limit += gas;178 }179180 pub fn evm_to_precompile_output(181 self,182 handle: &mut impl PrecompileHandle,183 result: execution::Result<Option<AbiWriter>>,184 ) -> Option<PrecompileResult> {185 use execution::Error;186 // We ignore error here, as it should not occur, as we have our own bookkeeping of gas187 let _ = handle.record_cost(self.initial_gas - self.gas_left());188 Some(match result {189 Ok(Some(v)) => Ok(PrecompileOutput {190 exit_status: ExitSucceed::Returned,191 output: v.finish(),192 }),193 Ok(None) => return None,194 Err(Error::Revert(e)) => {195 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));196 (&e as &str).abi_write(&mut writer);197198 Err(PrecompileFailure::Revert {199 exit_status: ExitRevert::Reverted,200 output: writer.finish(),201 })202 }203 Err(Error::Fatal(f)) => Err(PrecompileFailure::Fatal { exit_status: f }),204 Err(Error::Error(e)) => Err(e.into()),205 })206 }207208 /// Consume gas for reading.209 pub fn consume_store_reads(&self, reads: u64) -> execution::Result<()> {210 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(211 <T as frame_system::Config>::DbWeight::get()212 .read213 .saturating_mul(reads),214 // TODO: measure proof215 0,216 )))217 }218219 /// Consume gas for writing.220 pub fn consume_store_writes(&self, writes: u64) -> execution::Result<()> {221 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(222 <T as frame_system::Config>::DbWeight::get()223 .write224 .saturating_mul(writes),225 // TODO: measure proof226 0,227 )))228 }229230 /// Consume gas for reading and writing.231 pub fn consume_store_reads_and_writes(&self, reads: u64, writes: u64) -> execution::Result<()> {232 let weight = <T as frame_system::Config>::DbWeight::get();233 let reads = weight.read.saturating_mul(reads);234 let writes = weight.read.saturating_mul(writes);235 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(236 reads.saturating_add(writes),237 // TODO: measure proof238 0,239 )))240 }241}242243pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> execution::Error {244 use execution::Error as ExError;245 match err {246 DispatchError::Module(ModuleError { index, error, .. })247 if index248 == T::PalletInfo::index::<Pallet<T>>()249 .expect("evm-coder-substrate is a pallet, which should be added to runtime")250 as u8 =>251 {252 let mut read = &error as &[u8];253 match Error::<T>::decode(&mut read) {254 Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),255 Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),256 _ => unreachable!("this pallet only defines two possible errors"),257 }258 }259 DispatchError::Module(ModuleError {260 message: Some(msg), ..261 }) => ExError::Revert(msg.into()),262 DispatchError::Module(ModuleError { index, error, .. }) => {263 ExError::Revert(format!("error {:?} in pallet {}", error, index))264 }265 e => ExError::Revert(format!("substrate error: {:?}", e)),266 }267}268269pub trait WithRecorder<T: Config> {270 fn recorder(&self) -> &SubstrateRecorder<T>;271 fn into_recorder(self) -> SubstrateRecorder<T>;272}273274/// Helper to simplify implementing bridge between evm-coder definitions and pallet-evm275pub fn call<T, C, E, H>(handle: &mut H, mut e: E) -> Option<PrecompileResult>276where277 T: Config,278 C: evm_coder::Call + PreDispatch,279 E: evm_coder::Callable<C> + WithRecorder<T>,280 H: PrecompileHandle,281 execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,282{283 let result = call_internal(284 handle.context().caller,285 &mut e,286 handle.context().apparent_value,287 handle.input(),288 );289 e.into_recorder().evm_to_precompile_output(handle, result)290}291292fn call_internal<T, C, E>(293 caller: H160,294 e: &mut E,295 value: Value,296 input: &[u8],297) -> execution::Result<Option<AbiWriter>>298where299 T: Config,300 C: evm_coder::Call + PreDispatch,301 E: Contract + evm_coder::Callable<C> + WithRecorder<T>,302 execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,303{304 let (selector, mut reader) = AbiReader::new_call(input)?;305 let call = C::parse(selector, &mut reader)?;306 if call.is_none() {307 let selector = u32::from_be_bytes(selector);308 return Err(format!("unrecognized selector: 0x{selector:0<8x}").into());309 }310 let call = call.unwrap();311312 let dispatch_info = call.dispatch_info();313 e.recorder()314 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;315316 match execution::ResultWithPostInfo::from(e.call(Msg {317 call,318 caller,319 value,320 })) {321 Ok(v) => {322 let unspent = v.post_info.calc_unspent(&dispatch_info);323 e.recorder()324 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));325 Ok(Some(v.data))326 }327 Err(v) => {328 let unspent = v.post_info.calc_unspent(&dispatch_info);329 e.recorder()330 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));331 Err(v.data)332 }333 }334}335336#[cfg(test)]337#[allow(dead_code)]338mod tests {339 use core::marker::PhantomData;340341 use evm_coder::ERC165Call;342 use frame_support::weights::Weight;343344 use crate::execution::PreDispatch;345346 #[derive(PreDispatch)]347 enum ExampleCall<T: super::Config> {348 ERC165Call(ERC165Call, PhantomData<fn() -> T>),349 OtherCall(ERC165Call),350351 #[weight(Weight::from_ref_time(a + b))]352 Example {353 a: u64,354 b: u64,355 },356 }357}pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -440,7 +440,7 @@
collection_id: CollectionId,
address: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
@@ -471,7 +471,7 @@
collection_id: CollectionId,
address: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
@@ -501,7 +501,7 @@
collection_id: CollectionId,
new_owner: T::AccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -533,7 +533,7 @@
collection_id: CollectionId,
new_admin_id: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -562,7 +562,7 @@
collection_id: CollectionId,
account_id: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -590,7 +590,7 @@
collection_id: CollectionId,
new_sponsor: T::AccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -617,7 +617,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = ensure_signed(origin)?;
@@ -640,7 +640,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -920,7 +920,7 @@
collection_id: CollectionId,
value: bool,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1175,7 +1175,7 @@
collection_id: CollectionId,
new_limit: CollectionLimits,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1202,7 +1202,7 @@
collection_id: CollectionId,
new_permission: CollectionPermissions,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1273,7 +1273,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
ensure_root(origin)?;
runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -100,12 +100,11 @@
}
fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
- collection.check_is_internal()?;
match collection.mode {
CollectionMode::ReFungible => {
@@ -122,7 +121,7 @@
}
fn dispatch(collection_id: CollectionId) -> Result<Self, DispatchError> {
- if collection_id == CollectionId(0) {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
return Ok(Self::NativeFungible(NativeFungibleHandle::new()));
}
@@ -188,7 +187,7 @@
}
fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
if let Some(collection_id) = map_eth_to_id(&handle.code_address()) {
- if collection_id == CollectionId(0) {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
<NativeFungibleHandle<T>>::new().call(handle)
} else {
let collection = <CollectionHandle<T>>::new_with_gas_limit(