Skip to content

Commit a81265d

Browse files
authored
Cache code size/hash in storage (#893)
* cache code size/hash in storage * use in-memory code * don't clone metadata * bump evm * don't cache empty code metadata + tests * clippy * remove deprecated getter attribute * remove dep on sha3 crate * feedback
1 parent 4b1b168 commit a81265d

File tree

5 files changed

+100
-1
lines changed

5 files changed

+100
-1
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frame/evm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
1515
environmental = { workspace = true, optional = true }
1616
evm = { workspace = true, features = ["with-codec"] }
1717
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
18+
hex-literal = { version = "0.3.4" }
1819
impl-trait-for-tuples = "0.2.2"
1920
log = { workspace = true }
2021
rlp = { workspace = true }

frame/evm/src/lib.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ use frame_support::{
7676
};
7777
use frame_system::RawOrigin;
7878
use impl_trait_for_tuples::impl_for_tuples;
79-
use sp_core::{Hasher, H160, H256, U256};
79+
use scale_info::TypeInfo;
80+
use sp_core::{Decode, Encode, Hasher, H160, H256, U256};
8081
use sp_runtime::{
8182
traits::{BadOrigin, Saturating, UniqueSaturatedInto, Zero},
8283
AccountId32, DispatchErrorWithPostInfo,
@@ -512,6 +513,10 @@ pub mod pallet {
512513
#[pallet::storage]
513514
pub type AccountCodes<T: Config> = StorageMap<_, Blake2_128Concat, H160, Vec<u8>, ValueQuery>;
514515

516+
#[pallet::storage]
517+
pub type AccountCodesMetadata<T: Config> =
518+
StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>;
519+
515520
#[pallet::storage]
516521
pub type AccountStorages<T: Config> =
517522
StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>;
@@ -525,6 +530,21 @@ pub type BalanceOf<T> =
525530
type NegativeImbalanceOf<C, T> =
526531
<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
527532

533+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode, TypeInfo)]
534+
pub struct CodeMetadata {
535+
pub size: u64,
536+
pub hash: H256,
537+
}
538+
539+
impl CodeMetadata {
540+
fn from_code(code: &[u8]) -> Self {
541+
let size = code.len() as u64;
542+
let hash = H256::from(sp_io::hashing::keccak_256(code));
543+
544+
Self { size, hash }
545+
}
546+
}
547+
528548
pub trait EnsureAddressOrigin<OuterOrigin> {
529549
/// Success return type.
530550
type Success;
@@ -720,6 +740,7 @@ impl<T: Config> Pallet<T> {
720740
}
721741

722742
<AccountCodes<T>>::remove(address);
743+
<AccountCodesMetadata<T>>::remove(address);
723744
#[allow(deprecated)]
724745
let _ = <AccountStorages<T>>::remove_prefix(address, None);
725746
}
@@ -735,9 +756,40 @@ impl<T: Config> Pallet<T> {
735756
let _ = frame_system::Pallet::<T>::inc_sufficients(&account_id);
736757
}
737758

759+
// Update metadata.
760+
let meta = CodeMetadata::from_code(&code);
761+
<AccountCodesMetadata<T>>::insert(address, meta);
762+
738763
<AccountCodes<T>>::insert(address, code);
739764
}
740765

766+
/// Get the account metadata (hash and size) from storage if it exists,
767+
/// or compute it from code and store it if it doesn't exist.
768+
pub fn account_code_metadata(address: H160) -> CodeMetadata {
769+
if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
770+
return meta;
771+
}
772+
773+
let code = <AccountCodes<T>>::get(address);
774+
775+
// If code is empty we return precomputed hash for empty code.
776+
// We don't store it as this address could get code deployed in the future.
777+
if code.is_empty() {
778+
const EMPTY_CODE_HASH: [u8; 32] = hex_literal::hex!(
779+
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
780+
);
781+
return CodeMetadata {
782+
size: 0,
783+
hash: EMPTY_CODE_HASH.into(),
784+
};
785+
}
786+
787+
let meta = CodeMetadata::from_code(&code);
788+
789+
<AccountCodesMetadata<T>>::insert(address, meta);
790+
meta
791+
}
792+
741793
/// Get the account basic in EVM format.
742794
pub fn account_basic(address: &H160) -> (Account, frame_support::weights::Weight) {
743795
let account_id = T::AddressMapping::into_account_id(*address);

frame/evm/src/runner/stack.rs

+8
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,14 @@ where
854854
self.substate
855855
.recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key)))
856856
}
857+
858+
fn code_size(&self, address: H160) -> U256 {
859+
U256::from(<Pallet<T>>::account_code_metadata(address).size)
860+
}
861+
862+
fn code_hash(&self, address: H160) -> H256 {
863+
<Pallet<T>>::account_code_metadata(address).hash
864+
}
857865
}
858866

859867
#[cfg(feature = "forbid-evm-reentrancy")]

frame/evm/src/tests.rs

+37
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,40 @@ fn eip3607_transaction_from_precompile() {
651651
.is_ok());
652652
});
653653
}
654+
655+
#[test]
656+
fn metadata_code_gets_cached() {
657+
new_test_ext().execute_with(|| {
658+
let address = H160::repeat_byte(0xaa);
659+
660+
crate::Pallet::<Test>::create_account(address, b"Exemple".to_vec());
661+
662+
let metadata = crate::Pallet::<Test>::account_code_metadata(address);
663+
assert_eq!(metadata.size, 7);
664+
assert_eq!(
665+
metadata.hash,
666+
hex_literal::hex!("e8396a990fe08f2402e64a00647e41dadf360ba078a59ba79f55e876e67ed4bc")
667+
.into()
668+
);
669+
670+
let metadata2 = <AccountCodesMetadata<Test>>::get(&address).expect("to have metadata set");
671+
assert_eq!(metadata, metadata2);
672+
});
673+
}
674+
675+
#[test]
676+
fn metadata_empty_dont_code_gets_cached() {
677+
new_test_ext().execute_with(|| {
678+
let address = H160::repeat_byte(0xaa);
679+
680+
let metadata = crate::Pallet::<Test>::account_code_metadata(address);
681+
assert_eq!(metadata.size, 0);
682+
assert_eq!(
683+
metadata.hash,
684+
hex_literal::hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
685+
.into()
686+
);
687+
688+
assert!(<AccountCodesMetadata<Test>>::get(&address).is_none());
689+
});
690+
}

0 commit comments

Comments
 (0)