use crate::{
address::{self, AddressMapper},
gas::GasMeter,
limits,
primitives::{ExecReturnValue, StorageDeposit},
pure_precompiles::{self, is_precompile},
runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo},
storage::{self, meter::Diff, WriteOutcome},
tracing::if_tracing,
transient_storage::TransientStorage,
BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, ConversionPrecision,
Error, Event, ImmutableData, ImmutableDataOf, Pallet as Contracts,
};
use alloc::vec::Vec;
use core::{fmt::Debug, marker::PhantomData, mem};
use frame_support::{
crypto::ecdsa::ECDSAExt,
dispatch::{DispatchResult, DispatchResultWithPostInfo},
storage::{with_transaction, TransactionOutcome},
traits::{
fungible::{Inspect, Mutate},
tokens::{Fortitude, Preservation},
Contains, FindAuthor, OriginTrait, Time,
},
weights::Weight,
Blake2_128Concat, BoundedVec, 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, H160, H256, U256,
};
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_runtime::{
traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, Zero},
DispatchError, SaturatedConversion,
};
#[cfg(test)]
mod tests;
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"));
pub enum Key {
Fix([u8; 32]),
Var(VarSizedKey),
}
impl Key {
#[cfg(feature = "runtime-benchmarks")]
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, 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()),
}
}
}
pub trait Ext: sealing::Sealed {
type T: Config;
fn call(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
to: &H160,
value: U256,
input_data: Vec<u8>,
allows_reentry: bool,
read_only: bool,
) -> Result<(), ExecError>;
fn delegate_call(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
address: H160,
input_data: Vec<u8>,
) -> Result<(), ExecError>;
fn instantiate(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
code: H256,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
) -> Result<H160, ExecError>;
fn terminate(&mut self, beneficiary: &H160) -> DispatchResult;
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 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 origin(&self) -> &Origin<Self::T>;
fn is_contract(&self, address: &H160) -> bool;
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 own_code_hash(&mut self) -> &H256;
fn caller_is_origin(&self) -> bool;
fn caller_is_root(&self) -> bool;
fn account_id(&self) -> &AccountIdOf<Self::T>;
fn address(&self) -> H160 {
<Self::T as Config>::AddressMapper::to_address(self.account_id())
}
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>;
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) -> Option<AccountIdOf<Self::T>>;
fn max_value_size(&self) -> u32;
fn get_weight_price(&self, weight: Weight) -> U256;
fn gas_meter(&self) -> &GasMeter<Self::T>;
fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T>;
fn charge_storage(&mut self, diff: &Diff);
fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo;
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], ()>;
#[cfg(any(test, feature = "runtime-benchmarks"))]
fn contract_info(&mut self) -> &mut ContractInfo<Self::T>;
#[cfg(feature = "runtime-benchmarks")]
fn transient_storage(&mut self) -> &mut TransientStorage<Self::T>;
fn set_code_hash(&mut self, hash: H256) -> DispatchResult;
fn is_read_only(&self) -> bool;
fn last_frame_output(&self) -> &ExecReturnValue;
fn last_frame_output_mut(&mut self) -> &mut ExecReturnValue;
}
#[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(code_hash: H256, gas_meter: &mut GasMeter<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>,
gas_meter: &'a mut GasMeter<T>,
storage_meter: &'a mut storage::meter::Meter<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>,
skip_transfer: bool,
_phantom: PhantomData<E>,
}
struct Frame<T: Config> {
account_id: T::AccountId,
contract_info: CachedContract<T>,
value_transferred: U256,
entry_point: ExportedFunction,
nested_gas: GasMeter<T>,
nested_storage: storage::meter::NestedMeter<T>,
allows_reentry: bool,
read_only: bool,
delegate: Option<DelegateInfo<T>>,
last_frame_output: ExecReturnValue,
}
struct DelegateInfo<T: Config> {
pub caller: Origin<T>,
pub callee: H160,
}
struct DelegatedCall<T: Config, E> {
executable: E,
caller: Origin<T>,
callee: H160,
}
enum FrameArgs<'a, T: Config, E> {
Call {
dest: T::AccountId,
cached_info: Option<ContractInfo<T>>,
delegated_call: Option<DelegatedCall<T, E>>,
},
Instantiate {
sender: T::AccountId,
executable: E,
salt: Option<&'a [u8; 32]>,
input_data: &'a [u8],
},
}
enum CachedContract<T: Config> {
Cached(ContractInfo<T>),
Invalidated,
Terminated,
}
impl<T: Config> Frame<T> {
fn contract_info(&mut self) -> &mut ContractInfo<T> {
self.contract_info.get(&self.account_id)
}
fn terminate(&mut self) -> ContractInfo<T> {
self.contract_info.terminate(&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 {
let contract = <ContractInfoOf<T>>::get(T::AddressMapper::to_address(account_id));
if let Some(contract) = contract {
*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 terminate(&mut self, account_id: &T::AccountId) -> ContractInfo<T> {
self.load(account_id);
get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated))
}
}
impl<'a, T, E> Stack<'a, T, E>
where
T: Config,
BalanceOf<T>: Into<U256> + TryFrom<U256>,
MomentOf<T>: Into<U256>,
E: Executable<T>,
T::Hash: frame_support::traits::IsType<H256>,
{
pub fn run_call(
origin: Origin<T>,
dest: H160,
gas_meter: &'a mut GasMeter<T>,
storage_meter: &'a mut storage::meter::Meter<T>,
value: U256,
input_data: Vec<u8>,
skip_transfer: bool,
) -> ExecResult {
let dest = T::AddressMapper::to_account_id(&dest);
if let Some((mut stack, executable)) = Self::new(
FrameArgs::Call { dest: dest.clone(), cached_info: None, delegated_call: None },
origin.clone(),
gas_meter,
storage_meter,
value,
skip_transfer,
)? {
stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output)
} else {
let result = Self::transfer_from_origin(&origin, &origin, &dest, value);
if_tracing(|t| {
t.enter_child_span(
origin.account_id().map(T::AddressMapper::to_address).unwrap_or_default(),
T::AddressMapper::to_address(&dest),
false,
false,
value,
&input_data,
Weight::zero(),
);
match result {
Ok(ref output) => t.exit_child_span(&output, Weight::zero()),
Err(e) => t.exit_child_span_with_error(e.error.into(), Weight::zero()),
}
});
result
}
}
pub fn run_instantiate(
origin: T::AccountId,
executable: E,
gas_meter: &'a mut GasMeter<T>,
storage_meter: &'a mut storage::meter::Meter<T>,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
skip_transfer: bool,
) -> Result<(H160, ExecReturnValue), ExecError> {
let (mut stack, executable) = Self::new(
FrameArgs::Instantiate {
sender: origin.clone(),
executable,
salt,
input_data: input_data.as_ref(),
},
Origin::from_account_id(origin),
gas_meter,
storage_meter,
value,
skip_transfer,
)?
.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE);
let address = T::AddressMapper::to_address(&stack.top_frame().account_id);
stack
.run(executable, input_data)
.map(|_| (address, stack.first_frame.last_frame_output))
}
#[cfg(feature = "runtime-benchmarks")]
pub fn bench_new_call(
dest: H160,
origin: Origin<T>,
gas_meter: &'a mut GasMeter<T>,
storage_meter: &'a mut storage::meter::Meter<T>,
value: BalanceOf<T>,
) -> (Self, E) {
Self::new(
FrameArgs::Call {
dest: T::AddressMapper::to_account_id(&dest),
cached_info: None,
delegated_call: None,
},
origin,
gas_meter,
storage_meter,
value.into(),
false,
)
.unwrap()
.unwrap()
}
fn new(
args: FrameArgs<T, E>,
origin: Origin<T>,
gas_meter: &'a mut GasMeter<T>,
storage_meter: &'a mut storage::meter::Meter<T>,
value: U256,
skip_transfer: bool,
) -> Result<Option<(Self, E)>, ExecError> {
origin.ensure_mapped()?;
let Some((first_frame, executable)) = Self::new_frame(
args,
value,
gas_meter,
Weight::max_value(),
storage_meter,
BalanceOf::<T>::max_value(),
false,
true,
)?
else {
return Ok(None);
};
let stack = Self {
origin,
gas_meter,
storage_meter,
timestamp: T::Time::now(),
block_number: <frame_system::Pallet<T>>::block_number(),
first_frame,
frames: Default::default(),
transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES),
skip_transfer,
_phantom: Default::default(),
};
Ok(Some((stack, executable)))
}
fn new_frame<S: storage::meter::State + Default + Debug>(
frame_args: FrameArgs<T, E>,
value_transferred: U256,
gas_meter: &mut GasMeter<T>,
gas_limit: Weight,
storage_meter: &mut storage::meter::GenericMeter<T, S>,
deposit_limit: BalanceOf<T>,
read_only: bool,
origin_is_caller: bool,
) -> Result<Option<(Frame<T>, E)>, ExecError> {
let (account_id, contract_info, executable, delegate, entry_point, nested_gas) =
match frame_args {
FrameArgs::Call { dest, cached_info, delegated_call } => {
let contract = if let Some(contract) = cached_info {
contract
} else {
if let Some(contract) =
<ContractInfoOf<T>>::get(T::AddressMapper::to_address(&dest))
{
contract
} else {
return Ok(None);
}
};
let mut nested_gas = gas_meter.nested(gas_limit);
let (executable, delegate_caller) = if let Some(DelegatedCall {
executable,
caller,
callee,
}) = delegated_call
{
(executable, Some(DelegateInfo { caller, callee }))
} else {
(E::from_storage(contract.code_hash, &mut nested_gas)?, None)
};
(
dest,
contract,
executable,
delegate_caller,
ExportedFunction::Call,
nested_gas,
)
},
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),
contract,
executable,
None,
ExportedFunction::Constructor,
gas_meter.nested(gas_limit),
)
},
};
let frame = Frame {
delegate,
value_transferred,
contract_info: CachedContract::Cached(contract_info),
account_id,
entry_point,
nested_gas,
nested_storage: storage_meter.nested(deposit_limit),
allows_reentry: true,
read_only,
last_frame_output: Default::default(),
};
Ok(Some((frame, executable)))
}
fn push_frame(
&mut self,
frame_args: FrameArgs<T, E>,
value_transferred: U256,
gas_limit: Weight,
deposit_limit: BalanceOf<T>,
read_only: bool,
) -> Result<Option<E>, 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)
{
<ContractInfoOf<T>>::insert(
T::AddressMapper::to_address(&frame.account_id),
contract.clone(),
);
}
let frame = top_frame_mut!(self);
let nested_gas = &mut frame.nested_gas;
let nested_storage = &mut frame.nested_storage;
if let Some((frame, executable)) = Self::new_frame(
frame_args,
value_transferred,
nested_gas,
gas_limit,
nested_storage,
deposit_limit,
read_only,
false,
)? {
self.frames.try_push(frame).map_err(|_| Error::<T>::MaxCallDepthReached)?;
Ok(Some(executable))
} else {
Ok(None)
}
}
fn run(&mut self, executable: E, input_data: Vec<u8>) -> Result<(), ExecError> {
let frame = self.top_frame();
let entry_point = frame.entry_point;
let delegated_code_hash =
if frame.delegate.is_some() { Some(*executable.code_hash()) } else { None };
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.is_some(),
frame.read_only,
frame.value_transferred,
&input_data,
frame.nested_gas.gas_left(),
);
});
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 do_transaction = || -> ExecResult {
let caller = self.caller();
let frame = top_frame_mut!(self);
let account_id = &frame.account_id.clone();
if entry_point == ExportedFunction::Constructor {
let origin = &self.origin.account_id()?;
let ed = <Contracts<T>>::min_balance();
frame.nested_storage.record_charge(&StorageDeposit::Charge(ed));
if self.skip_transfer {
T::Currency::set_balance(account_id, ed);
} else {
T::Currency::transfer(origin, account_id, ed, Preservation::Preserve)?;
}
<System<T>>::inc_consumers(account_id)?;
<System<T>>::inc_account_nonce(caller.account_id()?);
<CodeInfo<T>>::increment_refcount(*executable.code_hash())?;
}
if delegated_code_hash.is_none() {
Self::transfer_from_origin(
&self.origin,
&caller,
&frame.account_id,
frame.value_transferred,
)?;
}
let code_deposit = executable.code_info().deposit();
let output = executable
.execute(self, entry_point, input_data)
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?;
if output.did_revert() {
return Ok(output);
}
let frame = self.top_frame_mut();
if entry_point == ExportedFunction::Constructor {
let deposit = frame.contract_info().update_base_deposit(code_deposit);
frame
.nested_storage
.charge_deposit(frame.account_id.clone(), StorageDeposit::Charge(deposit));
}
let contract = frame.contract_info.as_contract();
frame
.nested_storage
.enforce_limit(contract)
.map_err(|e| ExecError { error: e, origin: ErrorOrigin::Callee })?;
Ok(output)
};
let transaction_outcome =
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
let output = 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 gas_consumed = top_frame!(self).nested_gas.gas_consumed();
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 gas_consumed = top_frame!(self).nested_gas.gas_consumed();
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();
}
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);
prev.nested_gas.absorb_nested(frame.nested_gas);
if !persist {
return;
}
frame.contract_info.load(account_id);
let mut contract = frame.contract_info.into_contract();
prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut());
if let Some(contract) = contract {
if prev.account_id == *account_id {
prev.contract_info = CachedContract::Cached(contract);
return;
}
<ContractInfoOf<T>>::insert(T::AddressMapper::to_address(account_id), contract);
if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) {
c.contract_info = CachedContract::Invalidated;
}
}
} else {
self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas));
if !persist {
return;
}
let mut contract = self.first_frame.contract_info.as_contract();
self.storage_meter.absorb(
mem::take(&mut self.first_frame.nested_storage),
&self.first_frame.account_id,
contract.as_deref_mut(),
);
if let Some(contract) = contract {
<ContractInfoOf<T>>::insert(
T::AddressMapper::to_address(&self.first_frame.account_id),
contract,
);
}
}
}
fn transfer(
origin: &Origin<T>,
from: &T::AccountId,
to: &T::AccountId,
value: U256,
) -> ExecResult {
let value = crate::Pallet::<T>::convert_evm_to_native(value, ConversionPrecision::Exact)?;
if value.is_zero() {
return Ok(Default::default());
}
if <System<T>>::account_exists(to) {
return T::Currency::transfer(from, to, value, Preservation::Preserve)
.map(|_| Default::default())
.map_err(|_| Error::<T>::TransferFailed.into());
}
let origin = origin.account_id()?;
let ed = <T as Config>::Currency::minimum_balance();
with_transaction(|| -> TransactionOutcome<ExecResult> {
match T::Currency::transfer(origin, to, ed, Preservation::Preserve)
.and_then(|_| T::Currency::transfer(from, to, value, Preservation::Preserve))
{
Ok(_) => TransactionOutcome::Commit(Ok(Default::default())),
Err(_) => TransactionOutcome::Rollback(Err(Error::<T>::TransferFailed.into())),
}
})
}
fn transfer_from_origin(
origin: &Origin<T>,
from: &Origin<T>,
to: &T::AccountId,
value: U256,
) -> 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)
}
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 is_recursive(&self) -> bool {
let account_id = &self.top_frame().account_id;
self.frames().skip(1).any(|f| &f.account_id == account_id)
}
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 {
crate::Pallet::<T>::convert_native_to_evm(T::Currency::reducible_balance(
who,
Preservation::Preserve,
Fortitude::Polite,
))
}
#[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;
}
Some(System::<T>::block_hash(&block_number).into())
}
fn run_precompile(
&mut self,
precompile_address: H160,
is_delegate: bool,
is_read_only: bool,
value_transferred: U256,
input_data: &[u8],
) -> Result<(), ExecError> {
if_tracing(|tracer| {
tracer.enter_child_span(
self.caller().account_id().map(T::AddressMapper::to_address).unwrap_or_default(),
precompile_address,
is_delegate,
is_read_only,
value_transferred,
&input_data,
self.gas_meter().gas_left(),
);
});
let mut do_transaction = || -> ExecResult {
if !is_delegate {
Self::transfer_from_origin(
&self.origin,
&self.caller(),
&T::AddressMapper::to_fallback_account_id(&precompile_address),
value_transferred,
)?;
}
pure_precompiles::Precompiles::<T>::execute(
precompile_address,
self.gas_meter_mut(),
input_data,
)
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })
};
let transaction_outcome =
with_transaction(|| -> TransactionOutcome<Result<_, DispatchError>> {
let output = do_transaction();
match &output {
Ok(result) if !result.did_revert() => TransactionOutcome::Commit(Ok(output)),
_ => TransactionOutcome::Rollback(Ok(output)),
}
});
let output = match transaction_outcome {
Ok(output) => {
if_tracing(|tracer| {
let gas_consumed = top_frame!(self).nested_gas.gas_consumed();
match &output {
Ok(output) => tracer.exit_child_span(&output, gas_consumed),
Err(e) => tracer.exit_child_span_with_error(e.error.into(), gas_consumed),
}
});
output
},
Err(error) => {
if_tracing(|tracer| {
let gas_consumed = top_frame!(self).nested_gas.gas_consumed();
tracer.exit_child_span_with_error(error.into(), gas_consumed);
});
Err(error.into())
},
};
output.map(|output| {
self.top_frame_mut().last_frame_output = output;
})
}
}
impl<'a, T, E> Ext for Stack<'a, T, E>
where
T: Config,
E: Executable<T>,
BalanceOf<T>: Into<U256> + TryFrom<U256>,
MomentOf<T>: Into<U256>,
T::Hash: frame_support::traits::IsType<H256>,
{
type T = T;
fn call(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
dest_addr: &H160,
value: U256,
input_data: Vec<u8>,
allows_reentry: bool,
read_only: bool,
) -> Result<(), ExecError> {
self.top_frame_mut().allows_reentry = allows_reentry;
*self.last_frame_output_mut() = Default::default();
let try_call = || {
let is_read_only = read_only || self.is_read_only();
if is_precompile(dest_addr) {
return self.run_precompile(*dest_addr, false, is_read_only, value, &input_data);
}
let dest = T::AddressMapper::to_account_id(dest_addr);
if !self.allows_reentry(&dest) {
return Err(<Error<T>>::ReentranceDenied.into());
}
let value = value.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?;
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,
gas_limit,
deposit_limit.saturated_into::<BalanceOf<T>>(),
is_read_only,
)? {
self.run(executable, input_data)
} else {
let result = if is_read_only && value.is_zero() {
Ok(Default::default())
} else if is_read_only {
Err(Error::<T>::StateChangeDenied.into())
} else {
Self::transfer_from_origin(
&self.origin,
&Origin::from_account_id(self.account_id().clone()),
&dest,
value,
)
};
if_tracing(|t| {
t.enter_child_span(
T::AddressMapper::to_address(self.account_id()),
T::AddressMapper::to_address(&dest),
false,
is_read_only,
value,
&input_data,
Weight::zero(),
);
match result {
Ok(ref output) => t.exit_child_span(&output, Weight::zero()),
Err(e) => t.exit_child_span_with_error(e.error.into(), Weight::zero()),
}
});
result.map(|_| ())
}
};
let result = try_call();
self.top_frame_mut().allows_reentry = true;
result
}
fn delegate_call(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
address: H160,
input_data: Vec<u8>,
) -> Result<(), ExecError> {
if is_precompile(&address) {
return self.run_precompile(
address,
true,
self.is_read_only(),
0u32.into(),
&input_data,
);
}
*self.last_frame_output_mut() = Default::default();
let Some(info) = ContractInfoOf::<T>::get(&address) else { return Ok(()) };
let executable = E::from_storage(info.code_hash, self.gas_meter_mut())?;
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;
let executable = self.push_frame(
FrameArgs::Call {
dest: account_id,
cached_info: Some(contract_info),
delegated_call: Some(DelegatedCall {
executable,
caller: self.caller().clone(),
callee: address,
}),
},
value,
gas_limit,
deposit_limit.saturated_into::<BalanceOf<T>>(),
self.is_read_only(),
)?;
self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data)
}
fn instantiate(
&mut self,
gas_limit: Weight,
deposit_limit: U256,
code_hash: H256,
value: U256,
input_data: Vec<u8>,
salt: Option<&[u8; 32]>,
) -> Result<H160, ExecError> {
*self.last_frame_output_mut() = Default::default();
let executable = E::from_storage(code_hash, self.gas_meter_mut())?;
let sender = &self.top_frame().account_id;
let executable = self.push_frame(
FrameArgs::Instantiate {
sender: sender.clone(),
executable,
salt,
input_data: input_data.as_ref(),
},
value.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?,
gas_limit,
deposit_limit.saturated_into::<BalanceOf<T>>(),
self.is_read_only(),
)?;
let address = T::AddressMapper::to_address(&self.top_frame().account_id);
self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data)
.map(|_| address)
}
fn terminate(&mut self, beneficiary: &H160) -> DispatchResult {
if self.is_recursive() {
return Err(Error::<T>::TerminatedWhileReentrant.into());
}
let frame = self.top_frame_mut();
if frame.entry_point == ExportedFunction::Constructor {
return Err(Error::<T>::TerminatedInConstructor.into());
}
let info = frame.terminate();
let beneficiary_account = T::AddressMapper::to_account_id(beneficiary);
frame.nested_storage.terminate(&info, beneficiary_account);
info.queue_trie_for_deletion();
let account_address = T::AddressMapper::to_address(&frame.account_id);
ContractInfoOf::<T>::remove(&account_address);
ImmutableDataOf::<T>::remove(&account_address);
<CodeInfo<T>>::decrement_refcount(info.code_hash)?;
Ok(())
}
fn get_storage(&mut self, key: &Key) -> Option<Vec<u8>> {
self.top_frame_mut().contract_info().read(key)
}
fn get_storage_size(&mut self, key: &Key) -> Option<u32> {
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> {
let frame = self.top_frame_mut();
frame.contract_info.get(&frame.account_id).write(
key.into(),
value,
Some(&mut frame.nested_storage),
take_old,
)
}
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(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 origin(&self) -> &Origin<T> {
&self.origin
}
fn is_contract(&self, address: &H160) -> bool {
ContractInfoOf::<T>::contains_key(&address)
}
fn to_account_id(&self, address: &H160) -> T::AccountId {
T::AddressMapper::to_account_id(address)
}
fn code_hash(&self, address: &H160) -> H256 {
<ContractInfoOf<T>>::get(&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 {
<ContractInfoOf<T>>::get(&address)
.and_then(|contract| CodeInfoOf::<T>::get(contract.code_hash))
.map(|info| info.code_len())
.unwrap_or_default()
}
fn own_code_hash(&mut self) -> &H256 {
&self.top_frame_mut().contract_info().code_hash
}
fn caller_is_origin(&self) -> bool {
self.origin == self.caller()
}
fn caller_is_root(&self) -> bool {
self.caller_is_origin() && self.origin == Origin::Root
}
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(())
}
fn balance(&self) -> U256 {
self.account_balance(&self.top_frame().account_id)
}
fn balance_of(&self, address: &H160) -> U256 {
self.account_balance(&<Self::T as Config>::AddressMapper::to_account_id(address))
}
fn value_transferred(&self) -> U256 {
self.top_frame().value_transferred.into()
}
fn now(&self) -> U256 {
self.timestamp.into()
}
fn minimum_balance(&self) -> U256 {
T::Currency::minimum_balance().into()
}
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);
});
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) -> Option<AccountIdOf<Self::T>> {
let digest = <frame_system::Pallet<T>>::digest();
let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
T::FindAuthor::find_author(pre_runtime_digests)
}
fn max_value_size(&self) -> u32 {
limits::PAYLOAD_BYTES
}
fn get_weight_price(&self, weight: Weight) -> U256 {
T::WeightPrice::convert(weight).into()
}
fn gas_meter(&self) -> &GasMeter<Self::T> {
&self.top_frame().nested_gas
}
fn gas_meter_mut(&mut self) -> &mut GasMeter<Self::T> {
&mut self.top_frame_mut().nested_gas
}
fn charge_storage(&mut self, diff: &Diff) {
self.top_frame_mut().nested_storage.charge(diff)
}
fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo {
let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into();
origin.add_filter(T::CallFilter::contains);
call.dispatch(origin)
}
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], ()> {
ECDSAPublic::from(*pk).to_eth_address()
}
#[cfg(any(test, feature = "runtime-benchmarks"))]
fn contract_info(&mut self) -> &mut ContractInfo<Self::T> {
self.top_frame_mut().contract_info()
}
#[cfg(feature = "runtime-benchmarks")]
fn transient_storage(&mut self) -> &mut TransientStorage<Self::T> {
&mut self.transient_storage
}
fn set_code_hash(&mut self, hash: H256) -> DispatchResult {
let frame = top_frame_mut!(self);
let info = frame.contract_info();
let prev_hash = info.code_hash;
info.code_hash = hash;
let code_info = CodeInfoOf::<T>::get(hash).ok_or(Error::<T>::CodeNotFound)?;
let old_base_deposit = info.storage_base_deposit();
let new_base_deposit = info.update_base_deposit(code_info.deposit());
let deposit = StorageDeposit::Charge(new_base_deposit)
.saturating_sub(&StorageDeposit::Charge(old_base_deposit));
frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit);
<CodeInfo<T>>::increment_refcount(hash)?;
<CodeInfo<T>>::decrement_refcount(prev_hash)?;
Ok(())
}
fn is_read_only(&self) -> bool {
self.top_frame().read_only
}
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
}
}
mod sealing {
use super::*;
pub trait Sealed {}
impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {}
}