use crate::{
AccountInfoOf, AccountType, CodeInfoOf, Config, EthTransactError, LOG_TARGET, Pallet,
PristineCode,
address::AddressMapper,
evm::{StateOverride, StateOverrideSet, StorageOverride},
exec::{Executable, Key},
storage::{AccountInfo, ContractInfo},
vm::ContractBlob,
};
use alloc::{format, vec::Vec};
use frame_support::traits::Get;
use sp_core::{H160, U256};
use sp_runtime::DispatchError;
pub fn apply_state_overrides<T: Config>(overrides: StateOverrideSet) -> Result<(), EthTransactError>
where
T::Nonce: TryFrom<U256>,
{
log::trace!(
target: LOG_TARGET,
"applying state overrides for {} account(s)",
overrides.len(),
);
for (address, account_override) in overrides.0 {
apply_single_account_override::<T>(address, account_override)?;
}
Ok(())
}
fn apply_single_account_override<T: Config>(
address: H160,
overrides: StateOverride,
) -> Result<(), EthTransactError>
where
T::Nonce: TryFrom<U256>,
{
log::trace!(
target: LOG_TARGET,
"state override for {address:?}: balance={:?}, nonce={:?}, code={}, storage={}",
overrides.balance,
overrides.nonce,
overrides.code.is_some(),
overrides.storage.is_some(),
);
if let Some(balance) = overrides.balance {
apply_balance_override::<T>(&address, balance)?;
}
if let Some(nonce) = overrides.nonce {
apply_nonce_override::<T>(&address, nonce)?;
}
if let Some(code) = overrides.code {
apply_code_override::<T>(&address, code.0)?;
}
if let Some(storage) = overrides.storage {
apply_storage_override::<T>(&address, storage)?;
}
Ok(())
}
fn apply_balance_override<T: Config>(
address: &H160,
balance: U256,
) -> Result<(), EthTransactError> {
Pallet::<T>::set_evm_balance(address, balance).map_err(|err| {
EthTransactError::Message(format!("failed to override balance for {address:?}: {err:?}"))
})
}
fn apply_nonce_override<T: Config>(address: &H160, nonce: U256) -> Result<(), EthTransactError>
where
T::Nonce: TryFrom<U256>,
{
let nonce = nonce.try_into().map_err(|_| {
EthTransactError::Message(format!(
"nonce override for {address:?} exceeds the maximum representable value"
))
})?;
let account_id = T::AddressMapper::to_account_id(address);
frame_system::Account::<T>::mutate(&account_id, |account| {
account.nonce = nonce;
});
Ok(())
}
fn apply_code_override<T: Config>(address: &H160, code: Vec<u8>) -> Result<(), EthTransactError> {
let account_id = T::AddressMapper::to_account_id(address);
let is_pvm = code.starts_with(&polkavm_common::program::BLOB_MAGIC);
let module = if is_pvm {
ContractBlob::<T>::from_pvm_code(code, account_id.clone())
} else {
if !T::AllowEVMBytecode::get() {
return Err(EthTransactError::Message(format!(
"code override for {address:?} rejected: EVM bytecode is not allowed on this chain"
)));
}
ContractBlob::<T>::from_evm_runtime_code(code, account_id.clone())
}
.map_err(|err| {
EthTransactError::Message(format!(
"failed to create contract blob for code override on {address:?}: {err:?}"
))
})?;
let code_hash = *module.code_hash();
if !<CodeInfoOf<T>>::contains_key(code_hash) {
<PristineCode<T>>::insert(code_hash, module.code());
<CodeInfoOf<T>>::insert(code_hash, module.code_info().clone());
}
<AccountInfoOf<T>>::try_mutate(address, |account| -> Result<(), DispatchError> {
match account {
Some(AccountInfo { account_type: AccountType::Contract(contract), .. }) => {
contract.code_hash = code_hash;
},
_ => {
let nonce = frame_system::Pallet::<T>::account_nonce(&account_id);
let contract = ContractInfo::<T>::new(address, nonce, code_hash)?;
*account = Some(AccountInfo {
account_type: contract.into(),
dust: account.as_ref().map(|a| a.dust).unwrap_or(0),
});
},
}
Ok(())
})
.map_err(|err| {
EthTransactError::Message(format!("failed to apply code override for {address:?}: {err:?}"))
})
}
fn apply_storage_override<T: Config>(
address: &H160,
storage: StorageOverride,
) -> Result<(), EthTransactError> {
let contract = AccountInfo::<T>::load_contract(address).ok_or_else(|| {
EthTransactError::Message(format!(
"storage override for {address:?} failed: account is not a contract"
))
})?;
if let StorageOverride::State(_) = &storage {
let _ =
frame_support::storage::child::clear_storage(&contract.child_trie_info(), None, None);
}
let slots = match storage {
StorageOverride::State(slots) | StorageOverride::StateDiff(slots) => slots,
};
for (key, value) in slots {
contract
.write(&Key::from_fixed(key.0), Some(value.0.to_vec()), None, false)
.map_err(|err| {
EthTransactError::Message(format!(
"failed to write storage slot for {address:?}: {err:?}"
))
})?;
}
Ok(())
}