use crate::{
address::{self, AddressMapper},
evm::{block_storage, transfer_with_dust},
limits,
metering::{ChargedAmount, Diff, FrameMeter, ResourceMeter, State, Token, TransactionMeter},
precompiles::{All as AllPrecompiles, Instance as PrecompileInstance, Precompiles},
primitives::{ExecConfig, ExecReturnValue, StorageDeposit},
runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo},
storage::{AccountIdOrAddress, WriteOutcome},
tracing::if_tracing,
transient_storage::TransientStorage,
AccountInfo, AccountInfoOf, BalanceOf, BalanceWithDust, Code, CodeInfo, CodeInfoOf,
CodeRemoved, Config, ContractInfo, Error, Event, HoldReason, ImmutableData, ImmutableDataOf,
Pallet as Contracts, RuntimeCosts, TrieId, LOG_TARGET,
};
use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use core::{cmp, fmt::Debug, marker::PhantomData, mem, ops::ControlFlow};
use frame_support::{
crypto::ecdsa::ECDSAExt,
dispatch::DispatchResult,
ensure,
storage::{with_transaction, TransactionOutcome},
traits::{
fungible::{Inspect, Mutate},
tokens::Preservation,
Time,
},
weights::Weight,
Blake2_128Concat, BoundedVec, DebugNoBound, StorageHasher,
};
use frame_system::{
pallet_prelude::{BlockNumberFor, OriginFor},
Pallet as System, RawOrigin,
};
use sp_core::{
ecdsa::Public as ECDSAPublic,
sr25519::{Public as SR25519Public, Signature as SR25519Signature},
ConstU32, Get, H160, H256, U256,
};
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_runtime::{
traits::{BadOrigin, Saturating, TrailingZeroInput},
DispatchError, SaturatedConversion,
};
#[cfg(test)]
mod tests;
#[cfg(test)]
pub mod mock_ext;
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
pub type ExecResult = Result<ExecReturnValue, ExecError>;
type VarSizedKey = BoundedVec<u8, ConstU32<{ limits::STORAGE_KEY_BYTES }>>;
const FRAME_ALWAYS_EXISTS_ON_INSTANTIATE: &str = "The return value is only `None` if no contract exists at the specified address. This cannot happen on instantiate or delegate; qed";
pub const EMPTY_CODE_HASH: H256 =
H256(sp_core::hex2array!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"));
#[derive(Debug)]
pub enum Key {
Fix([u8; 32]),
Var(VarSizedKey),
}
impl Key {
pub fn unhashed(&self) -> &[u8] {
match self {
Key::Fix(v) => v.as_ref(),
Key::Var(v) => v.as_ref(),
}
}
pub fn hash(&self) -> Vec<u8> {
match self {
Key::Fix(v) => blake2_256(v.as_slice()).to_vec(),
Key::Var(v) => Blake2_128Concat::hash(v.as_slice()),
}
}
pub fn from_fixed(v: [u8; 32]) -> Self {
Self::Fix(v)
}
pub fn try_from_var(v: Vec<u8>) -> Result<Self, ()> {
VarSizedKey::try_from(v).map(Self::Var).map_err(|_| ())
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ReentrancyProtection {
AllowReentry,
Strict,
AllowNext,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)]
pub enum ErrorOrigin {
Caller,
Callee,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)]
pub struct ExecError {
pub error: DispatchError,
pub origin: ErrorOrigin,
}
impl<T: Into<DispatchError>> From<T> for ExecError {
fn from(error: T) -> Self {
Self { error: error.into(), origin: ErrorOrigin::Caller }
}
}
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)]
pub enum Origin<T: Config> {
Root,
Signed(T::AccountId),
}
impl<T: Config> Origin<T> {
pub fn from_account_id(account_id: T::AccountId) -> Self {
Origin::Signed(account_id)
}
pub fn from_runtime_origin(o: OriginFor<T>) -> Result<Self, DispatchError> {
match o.into() {
Ok(RawOrigin::Root) => Ok(Self::Root),
Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)),
_ => Err(BadOrigin.into()),
}
}
pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> {
match self {
Origin::Signed(id) => Ok(id),
Origin::Root => Err(DispatchError::RootNotAllowed),
}
}
fn ensure_mapped(&self) -> DispatchResult {
match self {
Self::Root => Ok(()),
Self::Signed(account_id) if T::AddressMapper::is_mapped(account_id) => Ok(()),
Self::Signed(_) => Err(<Error<T>>::AccountUnmapped.into()),
}
}
}
#[derive(DebugNoBound)]
pub enum CallResources<T: Config> {
NoLimits,
WeightDeposit { weight: Weight, deposit_limit: BalanceOf<T> },
Ethereum { gas: BalanceOf<T>, add_stipend: bool },
}
impl<T: Config> CallResources<T> {
pub fn from_weight_and_deposit(weight: Weight, deposit_limit: U256) -> Self {
Self::WeightDeposit {
weight,
deposit_limit: deposit_limit.saturated_into::<BalanceOf<T>>(),
}
}
pub fn from_ethereum_gas(gas: U256, add_stipend: bool) -> Self {
Self::Ethereum { gas: gas.saturated_into::<BalanceOf<T>>(), add_stipend }
}
}
impl<T: Config> Default for CallResources<T> {
fn default() -> Self {
Self::WeightDeposit { weight: Default::default(), deposit_limit: Default::default() }
}
}
struct TerminateArgs<T: Config> {
beneficiary: T::AccountId,
trie_id: TrieId,
code_hash: H256,
only_if_same_tx: bool,
}
pub trait Ext: PrecompileWithInfoExt {
fn delegate_call(
&mut self,
call_resources: &CallResources<Self::T>,
address: H160,
input_data: Vec<u8>,
) -> Result<(), ExecError>;
fn terminate_if_same_tx(&mut self, beneficiary: &H160) -> Result<CodeRemoved, DispatchError>;
#[allow(dead_code)]
fn own_code_hash(&mut self) -> &H256;
fn immutable_data_len(&mut self) -> u32;
fn get_immutable_data(&mut self) -> Result<ImmutableData, DispatchError>;
fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError>;
}
pub trait PrecompileWithInfoExt: PrecompileExt {
fn instantiate(
&mut self,
limits: &CallResources<Self::T>,
code: Code,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
) -> Result<H160, ExecError>;
}
pub trait PrecompileExt: sealing::Sealed {
type T: Config;
fn charge(&mut self, weight: Weight) -> Result<ChargedAmount, DispatchError> {
self.frame_meter_mut().charge_weight_token(RuntimeCosts::Precompile(weight))
}
fn adjust_gas(&mut self, charged: ChargedAmount, actual_weight: Weight) {
self.frame_meter_mut()
.adjust_weight(charged, RuntimeCosts::Precompile(actual_weight));
}
#[inline]
fn charge_or_halt<Tok: Token<Self::T>>(
&mut self,
token: Tok,
) -> ControlFlow<crate::vm::evm::Halt, ChargedAmount> {
self.frame_meter_mut().charge_or_halt(token)
}
fn call(
&mut self,
call_resources: &CallResources<Self::T>,
to: &H160,
value: U256,
input_data: Vec<u8>,
reentrancy: ReentrancyProtection,
read_only: bool,
) -> Result<(), ExecError>;
fn get_transient_storage(&self, key: &Key) -> Option<Vec<u8>>;
fn get_transient_storage_size(&self, key: &Key) -> Option<u32>;
fn set_transient_storage(
&mut self,
key: &Key,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;
fn caller(&self) -> Origin<Self::T>;
fn caller_of_caller(&self) -> Origin<Self::T>;
fn origin(&self) -> &Origin<Self::T>;
fn to_account_id(&self, address: &H160) -> AccountIdOf<Self::T>;
fn code_hash(&self, address: &H160) -> H256;
fn code_size(&self, address: &H160) -> u64;
fn caller_is_origin(&self, use_caller_of_caller: bool) -> bool;
fn caller_is_root(&self, use_caller_of_caller: bool) -> bool;
fn account_id(&self) -> &AccountIdOf<Self::T>;
fn address(&self) -> H160 {
<Self::T as Config>::AddressMapper::to_address(self.account_id())
}
fn balance(&self) -> U256;
fn balance_of(&self, address: &H160) -> U256;
fn value_transferred(&self) -> U256;
fn now(&self) -> U256;
fn minimum_balance(&self) -> U256;
fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>);
fn block_number(&self) -> U256;
fn block_hash(&self, block_number: U256) -> Option<H256>;
fn block_author(&self) -> H160;
fn gas_limit(&self) -> u64;
fn chain_id(&self) -> u64;
#[deprecated(note = "Renamed to `frame_meter`; this alias will be removed in future versions")]
fn gas_meter(&self) -> &FrameMeter<Self::T>;
#[deprecated(
note = "Renamed to `frame_meter_mut`; this alias will be removed in future versions"
)]
fn gas_meter_mut(&mut self) -> &mut FrameMeter<Self::T>;
fn frame_meter(&self) -> &FrameMeter<Self::T>;
fn frame_meter_mut(&mut self) -> &mut FrameMeter<Self::T>;
fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>;
fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool;
fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], DispatchError>;
#[cfg(any(test, feature = "runtime-benchmarks"))]
fn contract_info(&mut self) -> &mut ContractInfo<Self::T>;
#[cfg(any(feature = "runtime-benchmarks", test))]
fn transient_storage(&mut self) -> &mut TransientStorage<Self::T>;
fn is_read_only(&self) -> bool;
fn is_delegate_call(&self) -> bool;
fn last_frame_output(&self) -> &ExecReturnValue;
fn last_frame_output_mut(&mut self) -> &mut ExecReturnValue;
fn copy_code_slice(&mut self, buf: &mut [u8], address: &H160, code_offset: usize);
fn terminate_caller(&mut self, beneficiary: &H160) -> Result<(), DispatchError>;
fn effective_gas_price(&self) -> U256;
fn gas_left(&self) -> u64;
fn get_storage(&mut self, key: &Key) -> Option<Vec<u8>>;
fn get_storage_size(&mut self, key: &Key) -> Option<u32>;
fn set_storage(
&mut self,
key: &Key,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError>;
fn charge_storage(&mut self, diff: &Diff) -> DispatchResult;
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
sp_core::RuntimeDebug,
codec::Decode,
codec::Encode,
codec::MaxEncodedLen,
scale_info::TypeInfo,
)]
pub enum ExportedFunction {
Constructor,
Call,
}
pub trait Executable<T: Config>: Sized {
fn from_storage<S: State>(
code_hash: H256,
meter: &mut ResourceMeter<T, S>,
) -> Result<Self, DispatchError>;
fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError>;
fn execute<E: Ext<T = T>>(
self,
ext: &mut E,
function: ExportedFunction,
input_data: Vec<u8>,
) -> ExecResult;
fn code_info(&self) -> &CodeInfo<T>;
fn code(&self) -> &[u8];
fn code_hash(&self) -> &H256;
}
pub struct Stack<'a, T: Config, E> {
origin: Origin<T>,
transaction_meter: &'a mut TransactionMeter<T>,
timestamp: MomentOf<T>,
block_number: BlockNumberFor<T>,
frames: BoundedVec<Frame<T>, ConstU32<{ limits::CALL_STACK_DEPTH }>>,
first_frame: Frame<T>,
transient_storage: TransientStorage<T>,
exec_config: &'a ExecConfig<T>,
_phantom: PhantomData<E>,
}
struct Frame<T: Config> {
account_id: T::AccountId,
contract_info: CachedContract<T>,
value_transferred: U256,
entry_point: ExportedFunction,
frame_meter: FrameMeter<T>,
allows_reentry: bool,
read_only: bool,
delegate: Option<DelegateInfo<T>>,
last_frame_output: ExecReturnValue,
contracts_created: BTreeSet<T::AccountId>,
contracts_to_be_destroyed: BTreeMap<T::AccountId, TerminateArgs<T>>,
}
#[derive(Clone, RuntimeDebugNoBound)]
pub struct DelegateInfo<T: Config> {
pub caller: Origin<T>,
pub callee: H160,
}
enum ExecutableOrPrecompile<T: Config, E: Executable<T>, Env> {
Executable(E),
Precompile { instance: PrecompileInstance<Env>, _phantom: PhantomData<T> },
}
impl<T: Config, E: Executable<T>, Env> ExecutableOrPrecompile<T, E, Env> {
fn as_executable(&self) -> Option<&E> {
if let Self::Executable(executable) = self {
Some(executable)
} else {
None
}
}
fn is_pvm(&self) -> bool {
match self {
Self::Executable(e) => e.code_info().is_pvm(),
_ => false,
}
}
fn as_precompile(&self) -> Option<&PrecompileInstance<Env>> {
if let Self::Precompile { instance, .. } = self {
Some(instance)
} else {
None
}
}
#[cfg(any(feature = "runtime-benchmarks", test))]
fn into_executable(self) -> Option<E> {
if let Self::Executable(executable) = self {
Some(executable)
} else {
None
}
}
}
enum FrameArgs<'a, T: Config, E> {
Call {
dest: T::AccountId,
cached_info: Option<ContractInfo<T>>,
delegated_call: Option<DelegateInfo<T>>,
},
Instantiate {
sender: T::AccountId,
executable: E,
salt: Option<&'a [u8; 32]>,
input_data: &'a [u8],
},
}
enum CachedContract<T: Config> {
Cached(ContractInfo<T>),
Invalidated,
None,
}
impl<T: Config> Frame<T> {
fn contract_info(&mut self) -> &mut ContractInfo<T> {
self.contract_info.get(&self.account_id)
}
}
macro_rules! get_cached_or_panic_after_load {
($c:expr) => {{
if let CachedContract::Cached(contract) = $c {
contract
} else {
panic!(
"It is impossible to remove a contract that is on the call stack;\
See implementations of terminate;\
Therefore fetching a contract will never fail while using an account id
that is currently active on the call stack;\
qed"
);
}
}};
}
macro_rules! top_frame {
($stack:expr) => {
$stack.frames.last().unwrap_or(&$stack.first_frame)
};
}
macro_rules! top_frame_mut {
($stack:expr) => {
$stack.frames.last_mut().unwrap_or(&mut $stack.first_frame)
};
}
impl<T: Config> CachedContract<T> {
fn into_contract(self) -> Option<ContractInfo<T>> {
if let CachedContract::Cached(contract) = self {
Some(contract)
} else {
None
}
}
fn as_contract(&mut self) -> Option<&mut ContractInfo<T>> {
if let CachedContract::Cached(contract) = self {
Some(contract)
} else {
None
}
}
fn load(&mut self, account_id: &T::AccountId) {
if let CachedContract::Invalidated = self {
if let Some(contract) =
AccountInfo::<T>::load_contract(&T::AddressMapper::to_address(account_id))
{
*self = CachedContract::Cached(contract);
}
}
}
fn get(&mut self, account_id: &T::AccountId) -> &mut ContractInfo<T> {
self.load(account_id);
get_cached_or_panic_after_load!(self)
}
fn invalidate(&mut self) {
if matches!(self, CachedContract::Cached(_)) {
*self = CachedContract::Invalidated;
}
}
}
impl<'a, T, E> Stack<'a, T, E>
where
T: Config,
E: Executable<T>,
{
pub fn run_call(
origin: Origin<T>,
dest: H160,
transaction_meter: &'a mut TransactionMeter<T>,
value: U256,
input_data: Vec<u8>,
exec_config: &ExecConfig<T>,
) -> ExecResult {
let dest = T::AddressMapper::to_account_id(&dest);
if let Some((mut stack, executable)) = Stack::<'_, T, E>::new(
FrameArgs::Call { dest: dest.clone(), cached_info: None, delegated_call: None },
origin.clone(),
transaction_meter,
value,
exec_config,
&input_data,
)? {
stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output)
} else {
if_tracing(|t| {
t.enter_child_span(
origin.account_id().map(T::AddressMapper::to_address).unwrap_or_default(),
T::AddressMapper::to_address(&dest),
None,
false,
value,
&input_data,
Default::default(),
);
});
let result = if let Some(mock_answer) =
exec_config.mock_handler.as_ref().and_then(|handler| {
handler.mock_call(T::AddressMapper::to_address(&dest), &input_data, value)
}) {
Ok(mock_answer)
} else {
Self::transfer_from_origin(
&origin,
&origin,
&dest,
value,
transaction_meter,
exec_config,
)
};
if_tracing(|t| match result {
Ok(ref output) => t.exit_child_span(&output, Default::default()),
Err(e) => t.exit_child_span_with_error(e.error.into(), Default::default()),
});
log::trace!(target: LOG_TARGET, "call finished with: {result:?}");
result
}
}
pub fn run_instantiate(
origin: T::AccountId,
executable: E,
transaction_meter: &'a mut TransactionMeter<T>,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
exec_config: &ExecConfig<T>,
) -> Result<(H160, ExecReturnValue), ExecError> {
let deployer = T::AddressMapper::to_address(&origin);
let (mut stack, executable) = Stack::<'_, T, E>::new(
FrameArgs::Instantiate {
sender: origin.clone(),
executable,
salt,
input_data: input_data.as_ref(),
},
Origin::from_account_id(origin),
transaction_meter,
value,
exec_config,
&input_data,
)?
.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE);
let address = T::AddressMapper::to_address(&stack.top_frame().account_id);
let result = stack
.run(executable, input_data)
.map(|_| (address, stack.first_frame.last_frame_output));
if let Ok((contract, ref output)) = result {
if !output.did_revert() {
Contracts::<T>::deposit_event(Event::Instantiated { deployer, contract });
}
}
log::trace!(target: LOG_TARGET, "instantiate finished with: {result:?}");
result
}
#[cfg(any(feature = "runtime-benchmarks", test))]
pub fn bench_new_call(
dest: H160,
origin: Origin<T>,
transaction_meter: &'a mut TransactionMeter<T>,
value: BalanceOf<T>,
exec_config: &'a ExecConfig<T>,
) -> (Self, E) {
let call = Self::new(
FrameArgs::Call {
dest: T::AddressMapper::to_account_id(&dest),
cached_info: None,
delegated_call: None,
},
origin,
transaction_meter,
value.into(),
exec_config,
&Default::default(),
)
.unwrap()
.unwrap();
(call.0, call.1.into_executable().unwrap())
}
fn new(
args: FrameArgs<T, E>,
origin: Origin<T>,
transaction_meter: &'a mut TransactionMeter<T>,
value: U256,
exec_config: &'a ExecConfig<T>,
input_data: &Vec<u8>,
) -> Result<Option<(Self, ExecutableOrPrecompile<T, E, Self>)>, ExecError> {
origin.ensure_mapped()?;
let Some((first_frame, executable)) = Self::new_frame(
args,
value,
transaction_meter,
&CallResources::NoLimits,
false,
true,
input_data,
exec_config,
)?
else {
return Ok(None);
};
let mut timestamp = T::Time::now();
let mut block_number = <frame_system::Pallet<T>>::block_number();
if let Some(timestamp_override) =
exec_config.is_dry_run.as_ref().and_then(|cfg| cfg.timestamp_override)
{
block_number = block_number.saturating_add(1u32.into());
let delta = 1000u32.into();
timestamp = cmp::max(timestamp.saturating_add(delta), timestamp_override);
}
let stack = Self {
origin,
transaction_meter,
timestamp,
block_number,
first_frame,
frames: Default::default(),
transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES),
exec_config,
_phantom: Default::default(),
};
Ok(Some((stack, executable)))
}
fn new_frame<S: State>(
frame_args: FrameArgs<T, E>,
value_transferred: U256,
meter: &mut ResourceMeter<T, S>,
call_resources: &CallResources<T>,
read_only: bool,
origin_is_caller: bool,
input_data: &[u8],
exec_config: &ExecConfig<T>,
) -> Result<Option<(Frame<T>, ExecutableOrPrecompile<T, E, Self>)>, ExecError> {
let (account_id, contract_info, executable, delegate, entry_point) = match frame_args {
FrameArgs::Call { dest, cached_info, delegated_call } => {
let address = T::AddressMapper::to_address(&dest);
let precompile = <AllPrecompiles<T>>::get(address.as_fixed_bytes());
let mut contract = match (cached_info, &precompile) {
(Some(info), _) => CachedContract::Cached(info),
(None, None) =>
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
CachedContract::Cached(info)
} else {
return Ok(None);
},
(None, Some(precompile)) if precompile.has_contract_info() => {
log::trace!(target: LOG_TARGET, "found precompile for address {address:?}");
if let Some(info) = AccountInfo::<T>::load_contract(&address) {
CachedContract::Cached(info)
} else {
let info = ContractInfo::new(&address, 0u32.into(), H256::zero())?;
CachedContract::Cached(info)
}
},
(None, Some(_)) => CachedContract::None,
};
let delegated_call = delegated_call.or_else(|| {
exec_config.mock_handler.as_ref().and_then(|mock_handler| {
mock_handler.mock_delegated_caller(address, input_data)
})
});
let executable = if let Some(delegated_call) = &delegated_call {
if let Some(precompile) =
<AllPrecompiles<T>>::get(delegated_call.callee.as_fixed_bytes())
{
ExecutableOrPrecompile::Precompile {
instance: precompile,
_phantom: Default::default(),
}
} else {
let Some(info) = AccountInfo::<T>::load_contract(&delegated_call.callee)
else {
return Ok(None);
};
let executable = E::from_storage(info.code_hash, meter)?;
ExecutableOrPrecompile::Executable(executable)
}
} else {
if let Some(precompile) = precompile {
ExecutableOrPrecompile::Precompile {
instance: precompile,
_phantom: Default::default(),
}
} else {
let executable = E::from_storage(
contract
.as_contract()
.expect("When not a precompile the contract was loaded above; qed")
.code_hash,
meter,
)?;
ExecutableOrPrecompile::Executable(executable)
}
};
(dest, contract, executable, delegated_call, ExportedFunction::Call)
},
FrameArgs::Instantiate { sender, executable, salt, input_data } => {
let deployer = T::AddressMapper::to_address(&sender);
let account_nonce = <System<T>>::account_nonce(&sender);
let address = if let Some(salt) = salt {
address::create2(&deployer, executable.code(), input_data, salt)
} else {
use sp_runtime::Saturating;
address::create1(
&deployer,
if origin_is_caller {
account_nonce.saturating_sub(1u32.into()).saturated_into()
} else {
account_nonce.saturated_into()
},
)
};
let contract = ContractInfo::new(
&address,
<System<T>>::account_nonce(&sender),
*executable.code_hash(),
)?;
(
T::AddressMapper::to_fallback_account_id(&address),
CachedContract::Cached(contract),
ExecutableOrPrecompile::Executable(executable),
None,
ExportedFunction::Constructor,
)
},
};
let frame = Frame {
delegate,
value_transferred,
contract_info,
account_id,
entry_point,
frame_meter: meter.new_nested(call_resources)?,
allows_reentry: true,
read_only,
last_frame_output: Default::default(),
contracts_created: Default::default(),
contracts_to_be_destroyed: Default::default(),
};
Ok(Some((frame, executable)))
}
fn push_frame(
&mut self,
frame_args: FrameArgs<T, E>,
value_transferred: U256,
call_resources: &CallResources<T>,
read_only: bool,
input_data: &[u8],
) -> Result<Option<ExecutableOrPrecompile<T, E, Self>>, ExecError> {
if self.frames.len() as u32 == limits::CALL_STACK_DEPTH {
return Err(Error::<T>::MaxCallDepthReached.into());
}
let frame = self.top_frame();
if let (CachedContract::Cached(contract), ExportedFunction::Call) =
(&frame.contract_info, frame.entry_point)
{
AccountInfo::<T>::insert_contract(
&T::AddressMapper::to_address(&frame.account_id),
contract.clone(),
);
}
let frame = top_frame_mut!(self);
let meter = &mut frame.frame_meter;
if let Some((frame, executable)) = Self::new_frame(
frame_args,
value_transferred,
meter,
call_resources,
read_only,
false,
input_data,
self.exec_config,
)? {
self.frames.try_push(frame).map_err(|_| Error::<T>::MaxCallDepthReached)?;
Ok(Some(executable))
} else {
Ok(None)
}
}
fn run(
&mut self,
executable: ExecutableOrPrecompile<T, E, Self>,
input_data: Vec<u8>,
) -> Result<(), ExecError> {
let frame = self.top_frame();
let entry_point = frame.entry_point;
let is_pvm = executable.is_pvm();
if_tracing(|tracer| {
tracer.enter_child_span(
self.caller().account_id().map(T::AddressMapper::to_address).unwrap_or_default(),
T::AddressMapper::to_address(&frame.account_id),
frame.delegate.as_ref().map(|delegate| delegate.callee),
frame.read_only,
frame.value_transferred,
&input_data,
frame.frame_meter.eth_gas_left().unwrap_or_default().into(),
);
});
let mock_answer = self.exec_config.mock_handler.as_ref().and_then(|handler| {
handler.mock_call(
frame
.delegate
.as_ref()
.map(|delegate| delegate.callee)
.unwrap_or(T::AddressMapper::to_address(&frame.account_id)),
&input_data,
frame.value_transferred,
)
});
let frames_len = self.frames.len();
if let Some(caller_frame) = match frames_len {
0 => None,
1 => Some(&mut self.first_frame.last_frame_output),
_ => self.frames.get_mut(frames_len - 2).map(|frame| &mut frame.last_frame_output),
} {
*caller_frame = Default::default();
}
self.transient_storage.start_transaction();
let is_first_frame = self.frames.is_empty();
let do_transaction = || -> ExecResult {
let caller = self.caller();
let bump_nonce = self.exec_config.bump_nonce;
let frame = top_frame_mut!(self);
let account_id = &frame.account_id.clone();
if u32::try_from(input_data.len())
.map(|len| len > limits::CALLDATA_BYTES)
.unwrap_or(true)
{
Err(<Error<T>>::CallDataTooLarge)?;
}
if entry_point == ExportedFunction::Constructor {
let origin = &self.origin.account_id()?;
if !frame_system::Pallet::<T>::account_exists(&account_id) {
let ed = <Contracts<T>>::min_balance();
frame.frame_meter.charge_deposit(&StorageDeposit::Charge(ed))?;
<Contracts<T>>::charge_deposit(None, origin, account_id, ed, self.exec_config)?;
}
<System<T>>::inc_consumers(account_id)?;
<System<T>>::inc_account_nonce(account_id);
if bump_nonce || !is_first_frame {
<System<T>>::inc_account_nonce(caller.account_id()?);
}
if is_pvm {
<CodeInfo<T>>::increment_refcount(
*executable
.as_executable()
.expect("Precompiles cannot be instantiated; qed")
.code_hash(),
)?;
}
}
if frame.delegate.is_none() {
Self::transfer_from_origin(
&self.origin,
&caller,
account_id,
frame.value_transferred,
&mut frame.frame_meter,
self.exec_config,
)?;
}
if let Some(precompile) = executable.as_precompile() {
if precompile.has_contract_info() &&
frame.delegate.is_none() &&
!<System<T>>::account_exists(account_id)
{
T::Currency::mint_into(account_id, T::Currency::minimum_balance())?;
<System<T>>::inc_consumers(account_id)?;
}
}
let mut code_deposit = executable
.as_executable()
.map(|exec| exec.code_info().deposit())
.unwrap_or_default();
let mut output = match executable {
ExecutableOrPrecompile::Executable(executable) =>
executable.execute(self, entry_point, input_data),
ExecutableOrPrecompile::Precompile { instance, .. } =>
instance.call(input_data, self),
}
.and_then(|output| {
if u32::try_from(output.data.len())
.map(|len| len > limits::CALLDATA_BYTES)
.unwrap_or(true)
{
Err(<Error<T>>::ReturnDataTooLarge)?;
}
Ok(output)
})
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?;
if output.did_revert() {
return Ok(output);
}
let frame = if entry_point == ExportedFunction::Constructor {
let origin = self.origin.account_id()?.clone();
let frame = top_frame_mut!(self);
if !is_pvm {
let data = if crate::tracing::if_tracing(|_| {}).is_none() &&
self.exec_config.is_dry_run.is_none()
{
core::mem::replace(&mut output.data, Default::default())
} else {
output.data.clone()
};
let mut module = crate::ContractBlob::<T>::from_evm_runtime_code(data, origin)?;
module.store_code(&self.exec_config, &mut frame.frame_meter)?;
code_deposit = module.code_info().deposit();
let contract_info = frame.contract_info();
contract_info.code_hash = *module.code_hash();
<CodeInfo<T>>::increment_refcount(contract_info.code_hash)?;
}
let deposit = frame.contract_info().update_base_deposit(code_deposit);
frame.frame_meter.charge_contract_deposit_and_transfer(
frame.account_id.clone(),
StorageDeposit::Charge(deposit),
)?;
frame
} else {
self.top_frame_mut()
};
let contract = frame.contract_info.as_contract();
frame
.frame_meter
.finalize(contract)
.map_err(|e| ExecError { error: e, origin: ErrorOrigin::Callee })?;
Ok(output)
};
let transaction_outcome =
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
let output = if let Some(mock_answer) = mock_answer {
Ok(mock_answer)
} else {
do_transaction()
};
match &output {
Ok(result) if !result.did_revert() =>
TransactionOutcome::Commit(Ok((true, output))),
_ => TransactionOutcome::Rollback(Ok((false, output))),
}
});
let (success, output) = match transaction_outcome {
Ok((success, output)) => {
if_tracing(|tracer| {
let frame_meter = &top_frame!(self).frame_meter;
let gas_consumed = if is_first_frame {
frame_meter.total_consumed_gas().into()
} else {
frame_meter.eth_gas_consumed().into()
};
match &output {
Ok(output) => tracer.exit_child_span(&output, gas_consumed),
Err(e) => tracer.exit_child_span_with_error(e.error.into(), gas_consumed),
}
});
(success, output)
},
Err(error) => {
if_tracing(|tracer| {
let frame_meter = &top_frame!(self).frame_meter;
let gas_consumed = if is_first_frame {
frame_meter.total_consumed_gas().into()
} else {
frame_meter.eth_gas_consumed().into()
};
tracer.exit_child_span_with_error(error.into(), gas_consumed);
});
(false, Err(error.into()))
},
};
if success {
self.transient_storage.commit_transaction();
} else {
self.transient_storage.rollback_transaction();
}
log::trace!(target: LOG_TARGET, "frame finished with: {output:?}");
self.pop_frame(success);
output.map(|output| {
self.top_frame_mut().last_frame_output = output;
})
}
fn pop_frame(&mut self, persist: bool) {
let frame = self.frames.pop();
if let Some(mut frame) = frame {
let account_id = &frame.account_id;
let prev = top_frame_mut!(self);
if !persist {
prev.frame_meter.absorb_weight_meter_only(frame.frame_meter);
return;
}
frame.contract_info.load(account_id);
let mut contract = frame.contract_info.into_contract();
prev.frame_meter
.absorb_all_meters(frame.frame_meter, account_id, contract.as_mut());
prev.contracts_created.extend(frame.contracts_created);
prev.contracts_to_be_destroyed.extend(frame.contracts_to_be_destroyed);
if let Some(contract) = contract {
if prev.account_id == *account_id {
prev.contract_info = CachedContract::Cached(contract);
return;
}
AccountInfo::<T>::insert_contract(
&T::AddressMapper::to_address(account_id),
contract,
);
if let Some(f) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) {
f.contract_info.invalidate();
}
}
} else {
if !persist {
self.transaction_meter
.absorb_weight_meter_only(mem::take(&mut self.first_frame.frame_meter));
return;
}
let mut contract = self.first_frame.contract_info.as_contract();
self.transaction_meter.absorb_all_meters(
mem::take(&mut self.first_frame.frame_meter),
&self.first_frame.account_id,
contract.as_deref_mut(),
);
if let Some(contract) = contract {
AccountInfo::<T>::insert_contract(
&T::AddressMapper::to_address(&self.first_frame.account_id),
contract.clone(),
);
}
let contracts_created = mem::take(&mut self.first_frame.contracts_created);
let contracts_to_destroy = mem::take(&mut self.first_frame.contracts_to_be_destroyed);
for (contract_account, args) in contracts_to_destroy {
if args.only_if_same_tx && !contracts_created.contains(&contract_account) {
continue;
}
Self::do_terminate(
&mut self.transaction_meter,
self.exec_config,
&contract_account,
&self.origin,
&args,
)
.ok();
}
}
}
fn transfer<S: State>(
origin: &Origin<T>,
from: &T::AccountId,
to: &T::AccountId,
value: U256,
preservation: Preservation,
meter: &mut ResourceMeter<T, S>,
exec_config: &ExecConfig<T>,
) -> DispatchResult {
let value = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(value)
.map_err(|_| Error::<T>::BalanceConversionFailed)?;
if value.is_zero() {
return Ok(());
}
if <System<T>>::account_exists(to) {
return transfer_with_dust::<T>(from, to, value, preservation)
}
let origin = origin.account_id()?;
let ed = <T as Config>::Currency::minimum_balance();
with_transaction(|| -> TransactionOutcome<DispatchResult> {
match meter
.charge_deposit(&StorageDeposit::Charge(ed))
.and_then(|_| <Contracts<T>>::charge_deposit(None, origin, to, ed, exec_config))
.and_then(|_| transfer_with_dust::<T>(from, to, value, preservation))
{
Ok(_) => TransactionOutcome::Commit(Ok(())),
Err(err) => TransactionOutcome::Rollback(Err(err)),
}
})
}
fn transfer_from_origin<S: State>(
origin: &Origin<T>,
from: &Origin<T>,
to: &T::AccountId,
value: U256,
meter: &mut ResourceMeter<T, S>,
exec_config: &ExecConfig<T>,
) -> ExecResult {
let from = match from {
Origin::Signed(caller) => caller,
Origin::Root if value.is_zero() => return Ok(Default::default()),
Origin::Root => return Err(DispatchError::RootNotAllowed.into()),
};
Self::transfer(origin, from, to, value, Preservation::Preserve, meter, exec_config)
.map(|_| Default::default())
.map_err(Into::into)
}
fn do_terminate(
transaction_meter: &mut TransactionMeter<T>,
exec_config: &ExecConfig<T>,
contract_account: &T::AccountId,
origin: &Origin<T>,
args: &TerminateArgs<T>,
) -> Result<(), DispatchError> {
use frame_support::traits::fungible::InspectHold;
let contract_address = T::AddressMapper::to_address(contract_account);
let mut delete_contract = |trie_id: &TrieId, code_hash: &H256| {
let refund = T::Currency::balance_on_hold(
&HoldReason::StorageDepositReserve.into(),
&contract_account,
);
<Contracts<T>>::refund_deposit(
HoldReason::StorageDepositReserve,
contract_account,
origin.account_id()?,
refund,
Some(exec_config),
)?;
System::<T>::dec_consumers(&contract_account);
Self::transfer(
origin,
contract_account,
origin.account_id()?,
Contracts::<T>::convert_native_to_evm(T::Currency::minimum_balance()),
Preservation::Expendable,
transaction_meter,
exec_config,
)?;
let balance = <Contracts<T>>::convert_native_to_evm(<AccountInfo<T>>::total_balance(
contract_address.into(),
));
Self::transfer(
origin,
contract_account,
&args.beneficiary,
balance,
Preservation::Expendable,
transaction_meter,
exec_config,
)?;
let _code_removed = <CodeInfo<T>>::decrement_refcount(*code_hash)?;
ContractInfo::<T>::queue_trie_for_deletion(trie_id.clone());
AccountInfoOf::<T>::remove(contract_address);
ImmutableDataOf::<T>::remove(contract_address);
transaction_meter.terminate(contract_account.clone(), refund);
Ok(())
};
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
match delete_contract(&args.trie_id, &args.code_hash) {
Ok(()) => {
log::trace!(target: LOG_TARGET, "Terminated {contract_address:?}");
TransactionOutcome::Commit(Ok(()))
},
Err(e) => {
log::debug!(target: LOG_TARGET, "Contract at {contract_address:?} failed to terminate: {e:?}");
TransactionOutcome::Rollback(Err(e))
},
}
})
}
fn top_frame(&self) -> &Frame<T> {
top_frame!(self)
}
fn top_frame_mut(&mut self) -> &mut Frame<T> {
top_frame_mut!(self)
}
fn frames(&self) -> impl Iterator<Item = &Frame<T>> {
core::iter::once(&self.first_frame).chain(&self.frames).rev()
}
fn frames_mut(&mut self) -> impl Iterator<Item = &mut Frame<T>> {
core::iter::once(&mut self.first_frame).chain(&mut self.frames).rev()
}
fn allows_reentry(&self, id: &T::AccountId) -> bool {
!self.frames().any(|f| &f.account_id == id && !f.allows_reentry)
}
fn account_balance(&self, who: &T::AccountId) -> U256 {
let balance = AccountInfo::<T>::balance_of(AccountIdOrAddress::AccountId(who.clone()));
crate::Pallet::<T>::convert_native_to_evm(balance)
}
#[cfg(feature = "runtime-benchmarks")]
pub(crate) fn override_export(&mut self, export: ExportedFunction) {
self.top_frame_mut().entry_point = export;
}
#[cfg(feature = "runtime-benchmarks")]
pub(crate) fn set_block_number(&mut self, block_number: BlockNumberFor<T>) {
self.block_number = block_number;
}
fn block_hash(&self, block_number: U256) -> Option<H256> {
let Ok(block_number) = BlockNumberFor::<T>::try_from(block_number) else {
return None;
};
if block_number >= self.block_number {
return None;
}
if block_number < self.block_number.saturating_sub(256u32.into()) {
return None;
}
match crate::Pallet::<T>::eth_block_hash_from_number(block_number.into()) {
Some(hash) => Some(hash),
None => {
use codec::Decode;
let block_hash = System::<T>::block_hash(&block_number);
Decode::decode(&mut TrailingZeroInput::new(block_hash.as_ref())).ok()
},
}
}
fn has_contract_info(&self) -> bool {
let address = self.address();
let precompile = <AllPrecompiles<T>>::get::<Stack<'_, T, E>>(address.as_fixed_bytes());
if let Some(precompile) = precompile {
return precompile.has_contract_info();
}
true
}
}
impl<'a, T, E> Ext for Stack<'a, T, E>
where
T: Config,
E: Executable<T>,
{
fn delegate_call(
&mut self,
call_resources: &CallResources<T>,
address: H160,
input_data: Vec<u8>,
) -> Result<(), ExecError> {
*self.last_frame_output_mut() = Default::default();
let top_frame = self.top_frame_mut();
let contract_info = top_frame.contract_info().clone();
let account_id = top_frame.account_id.clone();
let value = top_frame.value_transferred;
if let Some(executable) = self.push_frame(
FrameArgs::Call {
dest: account_id,
cached_info: Some(contract_info),
delegated_call: Some(DelegateInfo {
caller: self.caller().clone(),
callee: address,
}),
},
value,
call_resources,
self.is_read_only(),
&input_data,
)? {
self.run(executable, input_data)
} else {
Ok(())
}
}
fn terminate_if_same_tx(&mut self, beneficiary: &H160) -> Result<CodeRemoved, DispatchError> {
if_tracing(|tracer| {
let addr = T::AddressMapper::to_address(self.account_id());
tracer.terminate(
addr,
*beneficiary,
self.top_frame().frame_meter.eth_gas_left().unwrap_or_default().into(),
crate::Pallet::<T>::evm_balance(&addr),
);
});
let frame = top_frame_mut!(self);
let info = frame.contract_info();
let trie_id = info.trie_id.clone();
let code_hash = info.code_hash;
let contract_address = T::AddressMapper::to_address(&frame.account_id);
let beneficiary = T::AddressMapper::to_account_id(beneficiary);
Self::transfer(
&self.origin,
&frame.account_id,
&beneficiary,
<Contracts<T>>::evm_balance(&contract_address),
Preservation::Preserve,
&mut frame.frame_meter,
self.exec_config,
)?;
let account_id = frame.account_id.clone();
self.top_frame_mut().contracts_to_be_destroyed.insert(
account_id,
TerminateArgs { beneficiary, trie_id, code_hash, only_if_same_tx: true },
);
Ok(CodeRemoved::Yes)
}
fn own_code_hash(&mut self) -> &H256 {
&self.top_frame_mut().contract_info().code_hash
}
fn immutable_data_len(&mut self) -> u32 {
self.top_frame_mut().contract_info().immutable_data_len()
}
fn get_immutable_data(&mut self) -> Result<ImmutableData, DispatchError> {
if self.top_frame().entry_point == ExportedFunction::Constructor {
return Err(Error::<T>::InvalidImmutableAccess.into());
}
let address = self
.top_frame()
.delegate
.as_ref()
.map(|d| d.callee)
.unwrap_or(T::AddressMapper::to_address(self.account_id()));
Ok(<ImmutableDataOf<T>>::get(address).ok_or_else(|| Error::<T>::InvalidImmutableAccess)?)
}
fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError> {
let frame = self.top_frame_mut();
if frame.entry_point == ExportedFunction::Call || data.is_empty() {
return Err(Error::<T>::InvalidImmutableAccess.into());
}
frame.contract_info().set_immutable_data_len(data.len() as u32);
<ImmutableDataOf<T>>::insert(T::AddressMapper::to_address(&frame.account_id), &data);
Ok(())
}
}
impl<'a, T, E> PrecompileWithInfoExt for Stack<'a, T, E>
where
T: Config,
E: Executable<T>,
{
fn instantiate(
&mut self,
call_resources: &CallResources<T>,
mut code: Code,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
) -> Result<H160, ExecError> {
*self.last_frame_output_mut() = Default::default();
let sender = self.top_frame().account_id.clone();
let executable = {
let executable = match &mut code {
Code::Upload(initcode) => {
if !T::AllowEVMBytecode::get() {
return Err(<Error<T>>::CodeRejected.into());
}
ensure!(input_data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
let initcode = crate::tracing::if_tracing(|_| initcode.clone())
.unwrap_or_else(|| mem::take(initcode));
E::from_evm_init_code(initcode, sender.clone())?
},
Code::Existing(hash) => {
let executable = E::from_storage(*hash, self.frame_meter_mut())?;
ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
executable
},
};
self.push_frame(
FrameArgs::Instantiate {
sender,
executable,
salt,
input_data: input_data.as_ref(),
},
value,
call_resources,
self.is_read_only(),
&input_data,
)?
};
let executable = executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE);
let account_id = self.top_frame().account_id.clone();
self.top_frame_mut().contracts_created.insert(account_id);
let address = T::AddressMapper::to_address(&self.top_frame().account_id);
if_tracing(|t| t.instantiate_code(&code, salt));
self.run(executable, input_data).map(|_| address)
}
}
impl<'a, T, E> PrecompileExt for Stack<'a, T, E>
where
T: Config,
E: Executable<T>,
{
type T = T;
fn call(
&mut self,
call_resources: &CallResources<T>,
dest_addr: &H160,
value: U256,
input_data: Vec<u8>,
allows_reentry: ReentrancyProtection,
read_only: bool,
) -> Result<(), ExecError> {
if allows_reentry == ReentrancyProtection::Strict {
self.top_frame_mut().allows_reentry = false;
}
*self.last_frame_output_mut() = Default::default();
let try_call = || {
let is_read_only = read_only || self.is_read_only();
let dest = if <AllPrecompiles<T>>::get::<Self>(dest_addr.as_fixed_bytes()).is_some() {
T::AddressMapper::to_fallback_account_id(dest_addr)
} else {
T::AddressMapper::to_account_id(dest_addr)
};
if !self.allows_reentry(&dest) {
return Err(<Error<T>>::ReentranceDenied.into());
}
if allows_reentry == ReentrancyProtection::AllowNext {
self.top_frame_mut().allows_reentry = false;
}
let cached_info = self
.frames()
.find(|f| f.entry_point == ExportedFunction::Call && f.account_id == dest)
.and_then(|f| match &f.contract_info {
CachedContract::Cached(contract) => Some(contract.clone()),
_ => None,
});
if let Some(executable) = self.push_frame(
FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None },
value,
call_resources,
is_read_only,
&input_data,
)? {
self.run(executable, input_data)
} else {
if_tracing(|t| {
t.enter_child_span(
T::AddressMapper::to_address(self.account_id()),
T::AddressMapper::to_address(&dest),
None,
is_read_only,
value,
&input_data,
Default::default(),
);
});
let result = if let Some(mock_answer) =
self.exec_config.mock_handler.as_ref().and_then(|handler| {
handler.mock_call(T::AddressMapper::to_address(&dest), &input_data, value)
}) {
*self.last_frame_output_mut() = mock_answer.clone();
Ok(mock_answer)
} else if is_read_only && value.is_zero() {
Ok(Default::default())
} else if is_read_only {
Err(Error::<T>::StateChangeDenied.into())
} else {
let account_id = self.account_id().clone();
let frame = top_frame_mut!(self);
Self::transfer_from_origin(
&self.origin,
&Origin::from_account_id(account_id),
&dest,
value,
&mut frame.frame_meter,
self.exec_config,
)
};
if_tracing(|t| match result {
Ok(ref output) => t.exit_child_span(&output, Default::default()),
Err(e) => t.exit_child_span_with_error(e.error.into(), Default::default()),
});
result.map(|_| ())
}
};
let result = try_call();
self.top_frame_mut().allows_reentry = true;
result
}
fn get_transient_storage(&self, key: &Key) -> Option<Vec<u8>> {
self.transient_storage.read(self.account_id(), key)
}
fn get_transient_storage_size(&self, key: &Key) -> Option<u32> {
self.transient_storage
.read(self.account_id(), key)
.map(|value| value.len() as _)
}
fn set_transient_storage(
&mut self,
key: &Key,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
let account_id = self.account_id().clone();
self.transient_storage.write(&account_id, key, value, take_old)
}
fn account_id(&self) -> &T::AccountId {
&self.top_frame().account_id
}
fn caller(&self) -> Origin<T> {
if let Some(Ok(mock_caller)) = self
.exec_config
.mock_handler
.as_ref()
.and_then(|mock_handler| mock_handler.mock_caller(self.frames.len()))
.map(|mock_caller| Origin::<T>::from_runtime_origin(mock_caller))
{
return mock_caller;
}
if let Some(DelegateInfo { caller, .. }) = &self.top_frame().delegate {
caller.clone()
} else {
self.frames()
.nth(1)
.map(|f| Origin::from_account_id(f.account_id.clone()))
.unwrap_or(self.origin.clone())
}
}
fn caller_of_caller(&self) -> Origin<T> {
let caller_of_caller_frame = match self.frames().nth(2) {
None => return self.origin.clone(),
Some(frame) => frame,
};
if let Some(DelegateInfo { caller, .. }) = &caller_of_caller_frame.delegate {
caller.clone()
} else {
Origin::from_account_id(caller_of_caller_frame.account_id.clone())
}
}
fn origin(&self) -> &Origin<T> {
if let Some(mock_origin) = self
.exec_config
.mock_handler
.as_ref()
.and_then(|mock_handler| mock_handler.mock_origin())
{
return mock_origin;
}
&self.origin
}
fn to_account_id(&self, address: &H160) -> T::AccountId {
T::AddressMapper::to_account_id(address)
}
fn code_hash(&self, address: &H160) -> H256 {
if let Some(code) = <AllPrecompiles<T>>::code(address.as_fixed_bytes()) {
return sp_io::hashing::keccak_256(code).into()
}
<AccountInfo<T>>::load_contract(&address)
.map(|contract| contract.code_hash)
.unwrap_or_else(|| {
if System::<T>::account_exists(&T::AddressMapper::to_account_id(address)) {
return EMPTY_CODE_HASH;
}
H256::zero()
})
}
fn code_size(&self, address: &H160) -> u64 {
if let Some(code) = <AllPrecompiles<T>>::code(address.as_fixed_bytes()) {
return code.len() as u64
}
<AccountInfo<T>>::load_contract(&address)
.and_then(|contract| CodeInfoOf::<T>::get(contract.code_hash))
.map(|info| info.code_len())
.unwrap_or_default()
}
fn caller_is_origin(&self, use_caller_of_caller: bool) -> bool {
let caller = if use_caller_of_caller { self.caller_of_caller() } else { self.caller() };
self.origin == caller
}
fn caller_is_root(&self, use_caller_of_caller: bool) -> bool {
self.caller_is_origin(use_caller_of_caller) && self.origin == Origin::Root
}
fn balance(&self) -> U256 {
self.account_balance(&self.top_frame().account_id)
}
fn balance_of(&self, address: &H160) -> U256 {
let balance =
self.account_balance(&<Self::T as Config>::AddressMapper::to_account_id(address));
if_tracing(|tracer| {
tracer.balance_read(address, balance);
});
balance
}
fn value_transferred(&self) -> U256 {
self.top_frame().value_transferred.into()
}
fn now(&self) -> U256 {
(self.timestamp / 1000u32.into()).into()
}
fn minimum_balance(&self) -> U256 {
let min = T::Currency::minimum_balance();
crate::Pallet::<T>::convert_native_to_evm(min)
}
fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>) {
let contract = T::AddressMapper::to_address(self.account_id());
if_tracing(|tracer| {
tracer.log_event(contract, &topics, &data);
});
block_storage::capture_ethereum_log(&contract, &data, &topics);
Contracts::<Self::T>::deposit_event(Event::ContractEmitted { contract, data, topics });
}
fn block_number(&self) -> U256 {
self.block_number.into()
}
fn block_hash(&self, block_number: U256) -> Option<H256> {
self.block_hash(block_number)
}
fn block_author(&self) -> H160 {
Contracts::<Self::T>::block_author()
}
fn gas_limit(&self) -> u64 {
<Contracts<T>>::evm_block_gas_limit().saturated_into()
}
fn chain_id(&self) -> u64 {
<T as Config>::ChainId::get()
}
fn gas_meter(&self) -> &FrameMeter<Self::T> {
&self.top_frame().frame_meter
}
#[inline]
fn gas_meter_mut(&mut self) -> &mut FrameMeter<Self::T> {
&mut self.top_frame_mut().frame_meter
}
fn frame_meter(&self) -> &FrameMeter<Self::T> {
&self.top_frame().frame_meter
}
#[inline]
fn frame_meter_mut(&mut self) -> &mut FrameMeter<Self::T> {
&mut self.top_frame_mut().frame_meter
}
fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> {
secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ())
}
fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool {
sp_io::crypto::sr25519_verify(
&SR25519Signature::from(*signature),
message,
&SR25519Public::from(*pub_key),
)
}
fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], DispatchError> {
Ok(ECDSAPublic::from(*pk)
.to_eth_address()
.or_else(|()| Err(Error::<T>::EcdsaRecoveryFailed))?)
}
#[cfg(any(test, feature = "runtime-benchmarks"))]
fn contract_info(&mut self) -> &mut ContractInfo<Self::T> {
self.top_frame_mut().contract_info()
}
#[cfg(any(feature = "runtime-benchmarks", test))]
fn transient_storage(&mut self) -> &mut TransientStorage<Self::T> {
&mut self.transient_storage
}
fn is_read_only(&self) -> bool {
self.top_frame().read_only
}
fn is_delegate_call(&self) -> bool {
self.top_frame().delegate.is_some()
}
fn last_frame_output(&self) -> &ExecReturnValue {
&self.top_frame().last_frame_output
}
fn last_frame_output_mut(&mut self) -> &mut ExecReturnValue {
&mut self.top_frame_mut().last_frame_output
}
fn copy_code_slice(&mut self, buf: &mut [u8], address: &H160, code_offset: usize) {
let len = buf.len();
if len == 0 {
return;
}
let code_hash = self.code_hash(address);
let code = crate::PristineCode::<T>::get(&code_hash).unwrap_or_default();
let len = len.min(code.len().saturating_sub(code_offset));
if len > 0 {
buf[..len].copy_from_slice(&code[code_offset..code_offset + len]);
}
buf[len..].fill(0);
}
fn terminate_caller(&mut self, beneficiary: &H160) -> Result<(), DispatchError> {
ensure!(self.top_frame().delegate.is_none(), Error::<T>::PrecompileDelegateDenied);
let parent = self.frames_mut().nth(1).ok_or_else(|| Error::<T>::ContractNotFound)?;
ensure!(parent.entry_point == ExportedFunction::Call, Error::<T>::TerminatedInConstructor);
ensure!(parent.delegate.is_none(), Error::<T>::PrecompileDelegateDenied);
let info = parent.contract_info();
let trie_id = info.trie_id.clone();
let code_hash = info.code_hash;
let contract_address = T::AddressMapper::to_address(&parent.account_id);
let beneficiary = T::AddressMapper::to_account_id(beneficiary);
let parent_account_id = parent.account_id.clone();
Self::transfer(
&self.origin,
&parent_account_id,
&beneficiary,
<Contracts<T>>::evm_balance(&contract_address),
Preservation::Preserve,
&mut top_frame_mut!(self).frame_meter,
&self.exec_config,
)?;
let args = TerminateArgs { beneficiary, trie_id, code_hash, only_if_same_tx: false };
self.top_frame_mut().contracts_to_be_destroyed.insert(parent_account_id, args);
Ok(())
}
fn effective_gas_price(&self) -> U256 {
self.exec_config
.effective_gas_price
.unwrap_or_else(|| <Contracts<T>>::evm_base_fee())
}
fn gas_left(&self) -> u64 {
let frame = self.top_frame();
frame.frame_meter.eth_gas_left().unwrap_or_default().saturated_into::<u64>()
}
fn get_storage(&mut self, key: &Key) -> Option<Vec<u8>> {
assert!(self.has_contract_info());
self.top_frame_mut().contract_info().read(key)
}
fn get_storage_size(&mut self, key: &Key) -> Option<u32> {
assert!(self.has_contract_info());
self.top_frame_mut().contract_info().size(key.into())
}
fn set_storage(
&mut self,
key: &Key,
value: Option<Vec<u8>>,
take_old: bool,
) -> Result<WriteOutcome, DispatchError> {
assert!(self.has_contract_info());
let frame = self.top_frame_mut();
frame.contract_info.get(&frame.account_id).write(
key.into(),
value,
Some(&mut frame.frame_meter),
take_old,
)
}
fn charge_storage(&mut self, diff: &Diff) -> DispatchResult {
assert!(self.has_contract_info());
self.top_frame_mut().frame_meter.record_contract_storage_changes(diff)
}
}
pub fn is_precompile<T: Config, E: Executable<T>>(address: &H160) -> bool {
<AllPrecompiles<T>>::get::<Stack<'_, T, E>>(address.as_fixed_bytes()).is_some()
}
#[cfg(feature = "runtime-benchmarks")]
pub fn bench_do_terminate<T: Config>(
transaction_meter: &mut TransactionMeter<T>,
exec_config: &ExecConfig<T>,
contract_account: &T::AccountId,
origin: &Origin<T>,
beneficiary: T::AccountId,
trie_id: TrieId,
code_hash: H256,
only_if_same_tx: bool,
) -> Result<(), DispatchError> {
Stack::<T, crate::ContractBlob<T>>::do_terminate(
transaction_meter,
exec_config,
contract_account,
origin,
&TerminateArgs { beneficiary, trie_id, code_hash, only_if_same_tx },
)
}
mod sealing {
use super::*;
pub trait Sealed {}
impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {}
#[cfg(test)]
impl<T: Config> sealing::Sealed for mock_ext::MockExt<T> {}
}