Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix account metadata checks #1564

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 10 additions & 18 deletions frame/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ type NegativeImbalanceOf<C, T> = <C as Currency<AccountIdOf<T>>>::NegativeImbala
PartialEq,
Encode,
Decode,
Default,
TypeInfo,
MaxEncodedLen
)]
Expand Down Expand Up @@ -912,7 +913,9 @@ impl<T: Config> Pallet<T> {
/// Check whether an account is empty.
pub fn is_account_empty(address: &H160) -> bool {
let (account, _) = Self::account_basic(address);
let code_len = <AccountCodes<T>>::decode_len(address).unwrap_or(0);
let code_len = <AccountCodesMetadata<T>>::get(address)
.unwrap_or_default()
.size;

account.nonce == U256::zero() && account.balance == U256::zero() && code_len == 0
}
Expand Down Expand Up @@ -992,28 +995,17 @@ impl<T: Config> Pallet<T> {
/// Get the account metadata (hash and size) from storage if it exists,
/// or compute it from code and store it if it doesn't exist.
pub fn account_code_metadata(address: H160) -> CodeMetadata {
if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
return meta;
}

let code = <AccountCodes<T>>::get(address);

// If code is empty we return precomputed hash for empty code.
// We don't store it as this address could get code deployed in the future.
if code.is_empty() {
<AccountCodesMetadata<T>>::get(address).unwrap_or_else(|| {
// If there is no codeMetadata, we assume that the code is empty,
// we then return precomputed hash for empty code.
const EMPTY_CODE_HASH: [u8; 32] = hex_literal::hex!(
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
);
return CodeMetadata {
CodeMetadata {
size: 0,
hash: EMPTY_CODE_HASH.into(),
};
}

let meta = CodeMetadata::from_code(&code);

<AccountCodesMetadata<T>>::insert(address, meta);
meta
}
})
}

/// Get the account basic in EVM format.
Expand Down
40 changes: 13 additions & 27 deletions frame/evm/src/runner/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ where
//
// EIP-3607: https://eips.ethereum.org/EIPS/eip-3607
// Do not allow transactions for which `tx.sender` has any code deployed.
if is_transactional && !<AccountCodes<T>>::get(source).is_empty() {
if is_transactional
&& <AccountCodesMetadata<T>>::get(source)
.unwrap_or_default()
.size != 0
{
return Err(RunnerError {
error: Error::<T>::TransactionMustComeFromEOA,
weight,
Expand Down Expand Up @@ -840,7 +844,11 @@ where
}

fn code(&self, address: H160) -> Vec<u8> {
<AccountCodes<T>>::get(address)
if AccountCodesMetadata::<T>::contains_key(address) {
<AccountCodes<T>>::get(address)
} else {
Default::default()
}
}

fn storage(&self, address: H160, index: H256) -> H256 {
Expand Down Expand Up @@ -1006,12 +1014,6 @@ where
}

fn record_external_operation(&mut self, op: evm::ExternalOperation) -> Result<(), ExitError> {
let size_limit: u64 = self
.metadata()
.gasometer()
.config()
.create_contract_limit
.unwrap_or_default() as u64;
let (weight_info, recorded) = self.info_mut();

if let Some(weight_info) = weight_info {
Expand All @@ -1027,12 +1029,11 @@ where
// Transfers to EOAs with standard 21_000 gas limit are able to
// pay for this pov size.
weight_info.try_record_proof_size_or_fail(IS_EMPTY_CHECK_PROOF_SIZE)?;
if <AccountCodes<T>>::decode_len(address).unwrap_or(0) == 0 {
return Ok(());
}

// We shoudl record metadata read as well
weight_info
.try_record_proof_size_or_fail(ACCOUNT_CODES_METADATA_PROOF_SIZE)?;

if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
weight_info.try_record_proof_size_or_fail(meta.size)?;
} else if let Some(remaining_proof_size) =
Expand All @@ -1049,6 +1050,7 @@ where
// Refund unused proof size
weight_info.refund_proof_size(pre_size.saturating_sub(actual_size));
}

recorded.account_codes.push(address);
}
}
Expand Down Expand Up @@ -1108,12 +1110,6 @@ where
_ => None,
};

let size_limit: u64 = self
.metadata()
.gasometer()
.config()
.create_contract_limit
.unwrap_or_default() as u64;
let (weight_info, recorded) = self.info_mut();

if let Some(weight_info) = weight_info {
Expand All @@ -1132,16 +1128,6 @@ where

if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
weight_info.try_record_proof_size_or_fail(meta.size)?;
} else if let Some(remaining_proof_size) = weight_info.remaining_proof_size() {
let pre_size = remaining_proof_size.min(size_limit);
weight_info.try_record_proof_size_or_fail(pre_size)?;

let actual_size = Pallet::<T>::account_code_metadata(address).size;
if actual_size > pre_size {
return Err(ExitError::OutOfGas);
}
// Refund unused proof size
weight_info.refund_proof_size(pre_size.saturating_sub(actual_size));
}

Ok(())
Expand Down
9 changes: 4 additions & 5 deletions primitives/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,11 @@ impl WeightInfo {
}
}
pub fn remaining_proof_size(&self) -> Option<u64> {
if let (Some(proof_size_usage), Some(proof_size_limit)) =
(self.proof_size_usage, self.proof_size_limit)
{
return Some(proof_size_limit.saturating_sub(proof_size_usage));
if let Some(proof_size_limit) = self.proof_size_limit {
Some(proof_size_limit.saturating_sub(self.proof_size_usage.unwrap_or_default()))
} else {
None
}
None
}

pub fn remaining_ref_time(&self) -> Option<u64> {
Expand Down
Loading