Skip to content
Merged
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
8 changes: 7 additions & 1 deletion crates/humanode-runtime/src/frontier_precompiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use precompile_utils::EvmData;
use sp_core::{H160, U256};
use sp_std::marker::PhantomData;

use crate::{evm_swap, ConstU64};
use crate::{evm_swap, ConstU64, EvmAccountId};

/// A set of constant values used to indicate precompiles.
pub mod precompiles_constants {
Expand Down Expand Up @@ -187,6 +187,12 @@ where
}
}

impl<R> pallet_evm_system::IsPrecompile<EvmAccountId> for FrontierPrecompiles<R> {
fn is_precompile(account_id: &EvmAccountId) -> bool {
Self::used_addresses().contains(account_id)
}
}

pub struct FrontierPrecompilesAddresses<R>(PhantomData<R>);

impl<R: pallet_evm::Config> sp_core::TypedGet for FrontierPrecompilesAddresses<R> {
Expand Down
1 change: 1 addition & 0 deletions crates/humanode-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ impl pallet_evm_system::Config for Runtime {
type AccountId = EvmAccountId;
type Index = Index;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = FrontierPrecompiles<Self>;
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-currency-swap/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-dummy-precompiles-code/src/mock/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-dummy-precompiles-code/src/mock/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-evm-balances/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = u64;
type Index = u64;
type AccountData = AccountData<u64>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
17 changes: 16 additions & 1 deletion crates/pallet-evm-system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ pub mod pallet {
/// pallet does regardless).
type AccountData: Member + FullCodec + Clone + Default + TypeInfo + MaxEncodedLen;

/// Checks whether a given account is a precompile or not.
type IsPrecompile: IsPrecompile<<Self as Config>::AccountId>;

/// Handler for when a new account has just been created.
type OnNewAccount: OnNewAccount<<Self as Config>::AccountId>;

Expand Down Expand Up @@ -196,7 +199,7 @@ impl<T: Config> StoredMap<<T as Config>::AccountId, <T as Config>::AccountData>
Account::<T>::mutate(k, |a| a.data = data);
}
(None, true) => {
if nonce != <T as Config>::Index::zero() {
if nonce != <T as Config>::Index::zero() || T::IsPrecompile::is_precompile(k) {
Account::<T>::mutate(k, |a| a.data = Default::default());
} else {
Account::<T>::remove(k);
Expand Down Expand Up @@ -233,6 +236,18 @@ impl<T: Config> fp_evm::AccountProvider for Pallet<T> {
}
}

/// A trait that allows checking whether a given account is a precompile or not.
pub trait IsPrecompile<AccountId> {
/// Check if the given account is a precompile.
fn is_precompile(account_id: &AccountId) -> bool;
}

impl<AccountId> IsPrecompile<AccountId> for () {
fn is_precompile(_account_id: &AccountId) -> bool {
false
}
}

/// Interface to handle account creation.
pub trait OnNewAccount<AccountId> {
/// A new account `who` has been registered.
Expand Down
10 changes: 10 additions & 0 deletions crates/pallet-evm-system/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,21 @@ impl frame_system::Config for Test {
type MaxConsumers = ConstU32<16>;
}

mock! {
#[derive(Debug)]
pub IsPrecompile {}

impl IsPrecompile<H160> for IsPrecompile {
pub fn is_precompile(who: &H160) -> bool;
}
}

impl pallet_evm_system::Config for Test {
type RuntimeEvent = RuntimeEvent;
type AccountId = H160;
type Index = u64;
type AccountData = u64;
type IsPrecompile = MockIsPrecompile;
type OnNewAccount = MockDummyOnNewAccount;
type OnKilledAccount = MockDummyOnKilledAccount;
}
Expand Down
57 changes: 57 additions & 0 deletions crates/pallet-evm-system/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ fn try_mutate_exists_account_removed() {
assert!(EvmSystem::account_exists(&account_id));

// Set mock expectations.
let is_precompile_ctx = MockIsPrecompile::is_precompile_context();
is_precompile_ctx
.expect()
.once()
.with(predicate::eq(account_id))
.return_const(false);

let on_killed_account_ctx = MockDummyOnKilledAccount::on_killed_account_context();
on_killed_account_ctx
.expect()
Expand All @@ -257,6 +264,7 @@ fn try_mutate_exists_account_removed() {
}));

// Assert mock invocations.
is_precompile_ctx.checkpoint();
on_killed_account_ctx.checkpoint();
});
}
Expand Down Expand Up @@ -296,6 +304,55 @@ fn try_mutate_exists_account_retained() {
});
}

/// This test verifies that `try_mutate_exists` works as expected in case data was providing
/// and returned data is `None`, account is precompiled. As a result, the account has been retained.
#[test]
fn try_mutate_exists_precompiled_account_retained() {
new_test_ext().execute_with_ext(|_| {
// Prepare test data.
let precompile = H160::from_str("1000000000000000000000000000000000000001").unwrap();
let nonce = 0;
let data = 100;

let account_info = AccountInfo { nonce, data };
<Account<Test>>::insert(precompile, account_info);

// Check test preconditions.
assert!(EvmSystem::account_exists(&precompile));

// Set mock expectations.
let is_precompile_ctx = MockIsPrecompile::is_precompile_context();
is_precompile_ctx
.expect()
.once()
.with(predicate::eq(precompile))
.return_const(true);

// Set block number to enable events.
System::set_block_number(1);

// Invoke the function under test.
EvmSystem::try_mutate_exists(&precompile, |maybe_data| -> Result<(), DispatchError> {
*maybe_data = None;
Ok(())
})
.unwrap();

// Assert state changes.
assert!(EvmSystem::account_exists(&precompile));
assert_eq!(<Account<Test>>::get(precompile), AccountInfo::default());

// Assert that there is no a corresponding `KilledAccount` event.
assert!(System::events().iter().all(|record| record.event
!= RuntimeEvent::EvmSystem(Event::KilledAccount {
account: precompile,
})));

// Assert mock invocations.
is_precompile_ctx.checkpoint();
});
}

/// This test verifies that `try_mutate_exists` works as expected in case data wasn't providing
/// and returned data is `None`. As a result, the account hasn't been created.
#[test]
Expand Down
1 change: 1 addition & 0 deletions crates/pallet-native-to-evm-swap/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/precompile-evm-to-native-swap/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
1 change: 1 addition & 0 deletions crates/precompile-native-currency/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl pallet_evm_system::Config for Test {
type AccountId = EvmAccountId;
type Index = u64;
type AccountData = pallet_evm_balances::AccountData<Balance>;
type IsPrecompile = ();
type OnNewAccount = ();
type OnKilledAccount = ();
}
Expand Down
23 changes: 23 additions & 0 deletions utils/e2e-tests/ts/lib/substrateUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Common substrate utils.

import * as substrate from "../lib/substrate";
import { AnyJson } from "@polkadot/types-codec/types";

type SystemAccount = {
data: {
Expand All @@ -22,3 +23,25 @@ export const getNativeBalance = async (
// We should explicitly convert to native bigint for math operations.
return BigInt(free);
};

type Event = {
event: {
method: string;
section: string;
data: AnyJson;
};
};

/// A helper function to get events at specified block.
export const getEvents = async (
substrateApi: substrate.Api,
blockNumber: bigint,
) => {
const blockHash = await substrateApi.rpc.chain.getBlockHash(blockNumber);
const substrateApiAt = await substrateApi.at(blockHash);
const events = (await substrateApiAt.query["system"]?.[
"events"
]?.()) as unknown as Event[];

return events;
};
24 changes: 23 additions & 1 deletion utils/e2e-tests/ts/tests/swap/evmToNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { beforeEachWithCleanup } from "../../lib/lifecycle";
import evmSwap from "../../lib/abis/evmSwap";
import { decodeEventLog } from "viem";
import * as substrate from "../../lib/substrate";
import { getNativeBalance } from "../../lib/substrateUtils";
import { getEvents, getNativeBalance } from "../../lib/substrateUtils";

const evmToNativeSwapPrecompileAddress =
"0x0000000000000000000000000000000000000900";
Expand Down Expand Up @@ -136,5 +136,27 @@ describe("evm to native tokens swap", () => {
address: evmToNativeSwapPrecompileAddress,
});
expect(evmSwapPrecompileBalance).toEqual(0n);

const substrateEvents = await getEvents(
substrateApi,
swapTxReceipt.blockNumber,
);

substrateEvents.forEach((item) => {
const section = item.event.section;
const method = item.event.method;
const data = JSON.stringify(item.event.data);

expect([section, method, data]).not.toEqual([
"evmSystem",
"NewAccount",
JSON.stringify([evmToNativeSwapPrecompileAddress]),
]);
expect([section, method, data]).not.toEqual([
"evmSystem",
"KilledAccount",
JSON.stringify([evmToNativeSwapPrecompileAddress]),
]);
});
});
});