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
4 changes: 4 additions & 0 deletions crates/evm/core/src/backend/cow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ impl DatabaseExt for CowBackend<'_> {
self.backend.has_cheatcode_access(account)
}

fn cached_accounts(&self) -> Vec<Address> {
self.backend.cached_accounts()
}

fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
self.backend.to_mut().set_blockhash(block_number, block_hash);
}
Expand Down
19 changes: 19 additions & 0 deletions crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ pub trait DatabaseExt: Database<Error = DatabaseError> + DatabaseCommit + Debug
/// Returns `true` if the given account is allowed to execute cheatcodes
fn has_cheatcode_access(&self, account: &Address) -> bool;

/// Returns all accounts in the memory database cache
fn cached_accounts(&self) -> Vec<Address>;

/// Ensures that `account` is allowed to execute cheatcodes
///
/// Returns an error if [`Self::has_cheatcode_access`] returns `false`
Expand Down Expand Up @@ -1516,6 +1519,22 @@ impl DatabaseExt for Backend {
self.inner.cheatcode_access_accounts.contains(account)
}

fn cached_accounts(&self) -> Vec<Address> {
self.mem_db
.cache
.accounts
.iter()
.filter_map(|(addr, acc)| {
// Only include accounts with non-empty bytecode (actual contracts)
if acc.info.code.as_ref().is_some_and(|c| !c.is_empty()) {
Some(*addr)
} else {
None
}
})
.collect()
}

fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
if let Some(db) = self.active_fork_db_mut() {
db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
Expand Down
11 changes: 11 additions & 0 deletions crates/forge/tests/it/revive/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,14 @@ async fn test_contract_deployment_in_different_modes(#[case] runtime_mode: Reviv
);
TestConfig::with_filter(runner, filter).spec_id(SpecId::PRAGUE).run().await;
}

#[rstest]
#[case::pvm(ReviveRuntimeMode::Pvm)]
#[case::evm(ReviveRuntimeMode::Evm)]
#[tokio::test(flavor = "multi_thread")]
async fn test_initial_contract_deployment(#[case] runtime_mode: ReviveRuntimeMode) {
let runner = TEST_DATA_REVIVE.runner_revive(runtime_mode);
let filter =
Filter::new("testInitialContractMigration", "InitialMigrationTest", ".*/revive/.*");
TestConfig::with_filter(runner, filter).spec_id(SpecId::PRAGUE).run().await;
}
2 changes: 2 additions & 0 deletions crates/forge/tests/it/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ impl ForgeTestData {
// Create resolc config with resolc compilation enabled
let mut resolc_config = (*config).clone();
resolc_config.polkadot.resolc_compile = true;
resolc_config.polkadot.resolc =
Some(foundry_config::SolcReq::Version("0.4.1".parse().unwrap()));
let mut resolc_project = resolc_config.project().unwrap();

// Filter files compatible with resolc
Expand Down
27 changes: 21 additions & 6 deletions crates/revive-strategy/src/cheatcodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
}

let ctx = get_context_ref_mut(ccx.state.strategy.context.as_mut());
ctx.externalities.etch_call(target, newRuntimeBytecode, ccx.ecx)?;
ctx.externalities.etch_call(target, newRuntimeBytecode)?;

cheatcode.dyn_apply(ccx, executor)
}
Expand Down Expand Up @@ -564,7 +564,7 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {

if ctx.revive_startup_migration.is_allowed() && !ctx.using_revive {
tracing::info!("startup pallet-revive migration initiated");
select_revive(ctx, ecx);
select_revive(ctx, ecx, true);
ctx.revive_startup_migration.done();
tracing::info!("startup pallet-revive migration completed");
}
Expand Down Expand Up @@ -645,7 +645,7 @@ fn handle_polkadot_call(
ctx.runtime_mode = target_mode;
if !is_backend_switch {
// Migrate to the target mode (from standard EVM to Polkadot)
select_revive(ctx, data);
select_revive(ctx, data, false);
}
} else if ctx.using_revive {
// Switching BACK to Foundry EVM
Expand All @@ -654,7 +654,11 @@ fn handle_polkadot_call(
Ok(Default::default())
}

fn select_revive(ctx: &mut PvmCheatcodeInspectorStrategyContext, data: Ecx<'_, '_, '_>) {
fn select_revive(
ctx: &mut PvmCheatcodeInspectorStrategyContext,
data: Ecx<'_, '_, '_>,
migrate_all: bool,
) {
if ctx.using_revive {
tracing::info!("already using pallet-revive");
return;
Expand All @@ -677,9 +681,20 @@ fn select_revive(ctx: &mut PvmCheatcodeInspectorStrategyContext, data: Ecx<'_, '
<revive_env::Runtime as polkadot_sdk::pallet_revive::Config>::ChainId::set(
&data.cfg.chain_id,
);
let persistent_accounts = data.journaled_state.database.persistent_accounts().clone();
let test_contract_addr = data.journaled_state.database.get_test_contract_address();
for address in persistent_accounts.into_iter().chain([data.tx.caller]) {
let mut accounts = data.journaled_state.database.persistent_accounts().clone();
accounts.insert(data.tx.caller);

if migrate_all {
// Migrate all cached contracts
accounts.extend(
data.journaled_state
.database
.cached_accounts()
);
}

for address in accounts {
tracing::info!("Migrating account {:?} (is_test_contract: {})", address, test_contract_addr == Some(address));
let acc = data.journaled_state.load_account(address).expect("failed to load account");
let amount = acc.data.info.balance;
Expand Down
15 changes: 3 additions & 12 deletions crates/revive-strategy/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy_primitives::{Address, B256, Bytes, FixedBytes, U256};
use foundry_cheatcodes::{Ecx, Error, Result};
use foundry_cheatcodes::{Error, Result};
use polkadot_sdk::{
pallet_revive::{
self, AccountId32Mapper, AccountInfo, AddressMapper, BalanceOf, BytecodeType, ContractInfo,
Expand Down Expand Up @@ -146,17 +146,8 @@ impl TestEnv {
});
}

pub fn etch_call(
&mut self,
target: &Address,
new_runtime_code: &Bytes,
ecx: Ecx<'_, '_, '_>,
) -> Result {
pub fn etch_call(&mut self, target: &Address, new_runtime_code: &Bytes) -> Result {
self.0.lock().unwrap().externalities.execute_with(|| {
let origin_address = H160::from_slice(ecx.tx.caller.as_slice());
let origin_account =
AccountId32Mapper::<Runtime>::to_fallback_account_id(&origin_address);

let target_address = H160::from_slice(target.as_slice());
let target_account =
AccountId32Mapper::<Runtime>::to_fallback_account_id(&target_address);
Expand All @@ -165,7 +156,7 @@ impl TestEnv {
let code_type =
if code.starts_with(b"PVM\0") { BytecodeType::Pvm } else { BytecodeType::Evm };
let contract_blob = Pallet::<Runtime>::try_upload_code(
origin_account,
Pallet::<Runtime>::account_id(),
code,
code_type,
&mut ResourceMeter::new(pallet_revive::TransactionLimits::WeightAndDeposit {
Expand Down
15 changes: 15 additions & 0 deletions testdata/default/revive/EvmToReviveMigration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,18 @@ contract EvmReviveMigrationTest is DSTest {
assertEq(evmContract.get(), 250, "EVM contract should update in PVM mode");
}
}

contract InitialMigrationTest is DSTest {
Vm constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
SimpleStorage storageContract = new SimpleStorage();

function setUp() public {
storageContract.set(42);
uint256 val = storageContract.get();
assertEq(val, 42);
}

function testInitialContractMigration() public {
assertEq(storageContract.get(), 42);
}
}
Loading