Skip to content

Commit b6a78d1

Browse files
committed
etch support
Signed-off-by: Alexandru Gheorghe <[email protected]>
1 parent 8608a99 commit b6a78d1

File tree

8 files changed

+489
-322
lines changed

8 files changed

+489
-322
lines changed

Cargo.lock

Lines changed: 309 additions & 308 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/anvil-polkadot/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "bin/main.rs"
2020
[dependencies]
2121
# foundry internal
2222
substrate-runtime = { path = "substrate-runtime" }
23-
polkadot-sdk = {git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/inject_state", default-features = false, features = [
23+
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/etch_dependencies", default-features = false, features = [
2424
"sc-allocator",
2525
"sc-basic-authorship",
2626
"sc-block-builder",

crates/anvil-polkadot/substrate-runtime/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ license.workspace = true
1212
[dependencies]
1313
array-bytes = { version = "6.2.2", default-features = false }
1414
codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
15-
polkadot-sdk = {git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/inject_state", default-features = false, features = [
15+
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/etch_dependencies", default-features = false, features = [
1616
"pallet-balances",
1717
"pallet-revive",
1818
"pallet-sudo",
@@ -27,7 +27,7 @@ scale-info = { version = "2.11.6", default-features = false }
2727
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
2828

2929
[build-dependencies]
30-
polkadot-sdk = {git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/inject_state", default-features = false, optional = true, features = ["substrate-wasm-builder"] }
30+
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/etch_dependencies", default-features = false, optional = true, features = ["substrate-wasm-builder"] }
3131

3232
[features]
3333
default = ["std"]

crates/forge/tests/cli/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod revive_cmd;
2727
mod revive_compiler;
2828
mod revive_config;
2929
mod revive_create;
30+
mod revive_etch;
3031
mod revive_inspect;
3132
mod revive_mock_call;
3233
mod revive_mock_calls;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Just a copy of cheatcodes Prank.t.sol adapted to work with pvm backend.
2+
// The adaptions are only to switch back and forth between evm and pvm.
3+
forgetest!(mock_call, |prj, cmd| {
4+
prj.insert_ds_test();
5+
prj.insert_vm();
6+
prj.insert_console();
7+
prj.add_source(
8+
"MockCall.t.sol",
9+
r#"
10+
// SPDX-License-Identifier: MIT OR Apache-2.0
11+
pragma solidity ^0.8.18;
12+
13+
14+
import "./test.sol";
15+
import "./Vm.sol";
16+
import {console} from "./console.sol";
17+
18+
contract Adder {
19+
function add(uint256 a, uint256 b) public pure returns (uint256) {
20+
return a * b; // Intentional bug to verify etch works
21+
}
22+
}
23+
24+
contract NestedAdder {
25+
uint256 public inner_a;
26+
uint256 public inner_b;
27+
constructor(uint256 a, uint256 b) {
28+
inner_a = a;
29+
inner_b = b;
30+
}
31+
32+
function nested_call(address target) public returns (uint256) {
33+
// Perform the add call on the target contract address
34+
(bool success, bytes memory data) = target.call(abi.encodeWithSignature("add(uint256,uint256)", inner_a, inner_b));
35+
require(success, "Nested call failed");
36+
uint256 result = abi.decode(data, (uint256));
37+
assert(success);
38+
return result;
39+
}
40+
}
41+
42+
43+
contract EtchTest is DSTest {
44+
Vm constant vm = Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))));
45+
// This is the bytecode for the correct Adder contract above compiled with resolc.
46+
bytes constant code = hex"50564d00008c0c000000000000010700c13000c00080047106000000000e0000001c0000002a000000350000004700000063616c6c5f646174615f636f707963616c6c5f646174615f6c6f616463616c6c5f646174615f73697a657365616c5f72657475726e7365745f696d6d757461626c655f6461746176616c75655f7472616e7366657272656405110287920463616c6c879e066465706c6f79068bec25028a531a001f004b007c00a500ae00c100cd00fa005e01630192019701bb0212036003b103c0033c048d04b2046c05ec05f5050106180627063d0660061a0769079e07ab07c607ca07080850099511f07b10087b158475010a02013d0700000251050750100209501004b3009511807b10787b15707b1668951580008411e04911384911304911208000330740951820491128501006e6084911584911504911484911408317400a0501821750821858821948821a40d49808d4a707d4870752072e6417501008ec008217188218108219088216d49707d48609d47909989920d48707977720d497075107090050100a61056467330850100c75098377330833090a283e076467330850100e62098378330733093300100a03019511a07b10587b15507b16489515608411e064164916384916304916208000330740956820491628501012370839070000025317045f9517e08477e07b67186471837733080a010182671880771c977720140800000000f7021677ab8736330014951120fe7b10d8017b15d0017b16c8019515e0018411e04921b8014921b0014921a8014921a0018317a0010a05288d02501016a3049511c07b10387b15307b16289515408411f0647664173300189511f07b10087b156475330820330740330048288f0850121a3a063200828910828a18828b088288d4ba0ad4980bd4ab0b98bb20d4a909979920d4b9095209449511c07b10387b15307b16289515408411e06476838883170a01821718821810821908821a7b67187b68107b69087b6a9551c08210388215308216289511403200009511e0fe7b1018017b1510017b160801951520018411f0828310828208829a08828c829b7b1b20829410d32a067b1638d82a06d8cb00821b38dab006828b188298187b1838c93409c969087b1828d869087b1330d8340664b4821938c9b909c96909c98909c92a08c908087b1c38821a20c9ca0a8e8b88aa4085aa01db8b0a8f98821c288ecbdb980bd49c08db8b0a510ace0064768217387b17c0007b127b12c8008217307b17d0009517e0009518c0007b14087b14d80050101ce3fe8217e8007b17288217e0007b17208217f8007b17188217f0007b1710821838958720d88708821ac88a09d8a90ada880a821830c88a0ad88a08821b08c88b0b7b1a707b19687b1760951780009518607b1b7850101e8cfe821790008218980082198000821a88007b67307b68387b69207b6a288217107b67108217187b67188217207b678217287b67089551e0fe821018018215100182160801951120013200501020a602828a10828b18828c088289d4cb0bd4a908d4b808988820d4ba0a97aa20d4a80852083f9511d07b10287b15209515308411f0827a18827810827b0882777b177b1b087b181064187b1a18649750102280059551d08210288215209511303200008218108217087b87088217187b877b861082177b87189551808210788215708216689511800032008217b0018218b8018219a801821aa001d49808d4a707d487075207e70138070000024921380149213001492128017b1720014921580149215001492140010495176001951840019519200149214801501026c7fd82126001821768018218700182197801821b8001821c880182169001821a98017b1ad8007b16d0007b1cc8007b1bc0007b19f8007b18f0007b17e800951700019518e0009519c0007b12e000501028d801821700017b1738821708017b1730821710017b1728821718017b17209517a00050102ab6fc8217b8007b17188216b0008218a8007b1810821aa0007b1a088219207b19588219287b19508219307b19488219387b19407b17787b16707b1868951780009518609519407b1a6033002c9511807b10787b15707b1668951580008411f07b171082878292828b08829308957a207b1a18d87a06c86b0a7b1a08d8ba0cda660c828a10828818829410829918c8ca06d8a60cc88c0c7b1c7b18387b1a307b1b287b17207b19587b14507b13489517409518207b124033002428f7fd821918821b10821008d49b07d46008d47808988820d46707977720d4870752075d646482178800821898007b183882138000821a9000d3b706d8b70cd80308da680cc94a06c9c602d8c606d84a0a821c38c99c0cc9ac0cc96c0cc9b707c98707c90306d4c707d42608d47808d42707988820977720d487075107080050102e19640764685010302e048378836933073300320a03019511f87b103308100002838833070133093300340a03013308491718491710491708490732004911184911104901113307046418491108501038f402390804000256183f0b200304000240013308100002838833070133092433003a0a03019511e87b10107b15087b16828b188294188282828c08829a088295828610829810c8ca09c82503d85305c85909d3a900d8a90ada050ac86805c85a0ad85a00c8b404d88508c84808c88000d86a05d3b008d8b00bda850bd3a606d46808d3c906d8c90cd82305db6c05db8b0552051b7b737b79087b7a107b70188210108215088216951118320033003c9511b07b10487b15409515508411f0491130491128491120140700000000717b484e9518207b173833073300362815029511807b10787b15707b1668951580008411f08282828308828410828818829a829b08829c1082991864767b19187b1c107b1b087b1a7b18387b14307b132895174095182064197b122050103efcfe821750821858821940821a487b67107b68187b697b6a089551808210788215708216689511800032009511f87b10330750104067f89511f87b103307015010425af89511c07b10387b15307b16289515408411f064766417501044a2f95012460632008217108218188219821a087b67107b68187b697b6a089551c0821038821530821628951140320239080800025108c0f8330730000383770a0428b3f87c78017c797c7a027c7b03978808d4980897aa1097bb18d4ba0ad4a8087c79057c7a047c7b067c7c07979908d4a90997bb1097cc18d4cb0bd4b909979920d489027c79097c7a087c7b0a7c7c0b979908d4a90997bb1097cc18d4cb0bd4b9097c7a0d7c7b0c7c7c0e7c780f97aa08d4ba0a97cc10978818d4c808d4a808978820d498037c78117c7a107c7b127c7c13978808d4a80897bb1097cc18d4cb0bd4b8087c7a157c7b147c7c167c791797aa08d4ba0a97cc10979918d4c909d4a909979920d4890a7c78197c79187c7b1a7c7c1b978808d4980897bb1097cc18d4cb0bd4b8087c791d7c7b1c7c7c1e7c771f979908d4b90997cc10977718d4c707d49707977720d487076f776fa86f396f2a7b5a187b59107b58087b57821008821595111032009511d87b10207b15187b161082897b19088289087b1982851082861833082050104ad3006f686f59821a6faa821b086fbb787b18787a10787908787898bc38787c1f98bc30787c1e98bc28787c1d98bc20787c1c98bc18787c1b98bc10787c1a98bb08787b1998ab38787b1798ab30787b1698ab28787b1598ab20787b1498ab18787b1398ab10787b1298aa08787a11989a38787a0f989a30787a0e989a28787a0d989a20787a0c989a18787a0b989a10787a0a98990878790998893878790798893078790698892878790598892078790498891878790398891078790298880878780182102082151882161095112832008b7910520931c8780883881f8488e05638000001253309040002390a040002ae8a093d080400020133081000028377c887073200004969488424892421494892344992a490a4244992a1423515aa3449929290248448523549004944442422224a4892a4925492849294244992244955929294246944442449254992244992a424499294a494244a1489884488442291242549922489244444244992a42422220a494a9224499224a9a41492a449524a924411114922444444848888101111111111111111112112894422442212499224491249922491549224499224494992244992244992a4884424499224499224499251a14a5328a525a9242921443094a4494a4a4a922449922449922449929224a52449920490942449a4242529494992129224294912a5424a4892a4429494a424490a112a294992244992244992244992244992244992244992244992244992244992244992aa24a5942425250955294949922449922449922449922449922449922449922449529224082161280500";
47+
48+
// Test etching code into an existing contract instance works correctly.
49+
function testEtchExistingContract() public {
50+
vm.pvm(true);
51+
Adder adder = new Adder();
52+
53+
// Without etch, the add function is broken
54+
uint256 buggy_result = adder.add(1, 2);
55+
assertEq(buggy_result, 2);
56+
57+
// Etch the correct bytecode into the existing contract
58+
vm.etch(address(adder), code);
59+
uint256 result = adder.add(1, 2);
60+
assertEq(result, 3);
61+
62+
// Verify that nested calls also work correctly after etch
63+
uint256 nested_call_result = (new NestedAdder(1, 2)).nested_call(address(adder));
64+
assertEq(nested_call_result, 3);
65+
}
66+
67+
// Test etching code into any arbitrary address works correctly.
68+
function testEtchAnyContract() public {
69+
vm.pvm(true);
70+
// Etch the correct bytecode into an arbitrary address
71+
address target = address(7070707);
72+
vm.etch(target, code);
73+
(bool success, bytes memory output) = target.call(abi.encodeWithSignature("add(uint256,uint256)", 1, 2));
74+
uint256 result1 = abi.decode(output, (uint256));
75+
76+
assert(success);
77+
assertEq(result1, 3);
78+
79+
uint256 nested_call_result = (new NestedAdder(1, 2)).nested_call(address(target));
80+
assertEq(nested_call_result, 3);
81+
82+
// Etch into the zero address as well to verify it works for reserved addresses
83+
address target2 = address(0);
84+
vm.etch(target2, code);
85+
(bool success2, bytes memory output2) = target2.call(abi.encodeWithSignature("add(uint256,uint256)", 1, 2));
86+
uint256 result2 = abi.decode(output2, (uint256));
87+
88+
assert(success2);
89+
assertEq(result2, 3);
90+
91+
uint256 nested_call_result2 = (new NestedAdder(1, 2)).nested_call(address(target2));
92+
assertEq(nested_call_result2, 3);
93+
}
94+
}
95+
"#,
96+
)
97+
.unwrap();
98+
99+
let res = cmd.args(["test", "--resolc", "--resolc-startup"]).assert_success();
100+
101+
res.stderr_eq(str![""]).stdout_eq(str![[r#"
102+
[COMPILING_FILES] with [SOLC_VERSION]
103+
[SOLC_VERSION] [ELAPSED]
104+
Compiler run successful!
105+
[COMPILING_FILES] with [RESOLC_VERSION]
106+
[RESOLC_VERSION] [ELAPSED]
107+
Compiler run successful!
108+
109+
Ran 2 tests for src/MockCall.t.sol:EtchTest
110+
[PASS] testEtchAnyContract() ([GAS])
111+
[PASS] testEtchExistingContract() ([GAS])
112+
Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED]
113+
114+
Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests)
115+
116+
"#]]);
117+
});

crates/revive-env/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ std = ["polkadot-sdk/std"]
1616
codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
1717
scale-info = { version = "2.11.6", default-features = false }
1818

19-
polkadot-sdk = {git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/inject_state", features = [
19+
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/etch_dependencies", features = [
2020
"experimental",
2121
"runtime",
2222
"polkadot-runtime-common",

crates/revive-strategy/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ semver.workspace = true
3535

3636
codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
3737
scale-info = { version = "2.11.6", default-features = false }
38+
hex = "0.4"
3839

39-
polkadot-sdk = {git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/inject_state", features = [
40+
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "alexggh/etch_dependencies", features = [
4041
"experimental",
4142
"runtime",
4243
"polkadot-runtime-common",

crates/revive-strategy/src/cheatcodes/mod.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,34 @@ use alloy_sol_types::SolValue;
1111
use foundry_cheatcodes::{
1212
Broadcast, BroadcastableTransactions, CheatcodeInspectorStrategy,
1313
CheatcodeInspectorStrategyContext, CheatcodeInspectorStrategyRunner, CheatsConfig, CheatsCtxt,
14-
CommonCreateInput, DealRecord, Ecx, EvmCheatcodeInspectorStrategyRunner, Result,
15-
Vm::{dealCall, getNonce_0Call, pvmCall, rollCall, setNonceCall, setNonceUnsafeCall, warpCall},
14+
CommonCreateInput, DealRecord, Ecx, Error, EvmCheatcodeInspectorStrategyRunner, Result,
15+
Vm::{
16+
dealCall, etchCall, getNonce_0Call, pvmCall, rollCall, setNonceCall, setNonceUnsafeCall,
17+
warpCall,
18+
},
1619
};
1720

1821
use foundry_common::sh_err;
1922
use foundry_compilers::resolc::dual_compiled_contracts::DualCompiledContracts;
2023
use revive_env::{AccountId, Runtime, System, Timestamp};
2124

25+
use crate::{
26+
cheatcodes::mock_handler::MockHandlerImpl, execute_with_externalities, trace,
27+
tracing::apply_prestate_trace,
28+
};
29+
use alloy_eips::eip7702::SignedAuthorization;
2230
use polkadot_sdk::{
2331
frame_support::traits::{Currency, fungible::Mutate},
2432
pallet_balances,
2533
pallet_revive::{
26-
self, AddressMapper, BalanceOf, BalanceWithDust, Code, Config, ExecConfig, Pallet,
34+
self, AccountInfo, AddressMapper, BalanceOf, BalanceWithDust, Code, Config, ContractInfo,
35+
ExecConfig, Executable, Pallet,
2736
},
2837
polkadot_runtime_common::Bounded,
2938
polkadot_sdk_frame::prelude::OriginFor,
3039
sp_core::{self, H160},
3140
sp_weights::Weight,
3241
};
33-
34-
use crate::{
35-
cheatcodes::mock_handler::MockHandlerImpl, execute_with_externalities, trace,
36-
tracing::apply_prestate_trace,
37-
};
38-
use alloy_eips::eip7702::SignedAuthorization;
3942
use revm::{
4043
bytecode::opcode as op,
4144
context::{CreateScheme, JournalTr},
@@ -157,6 +160,44 @@ fn set_block_number(new_height: U256, ecx: Ecx<'_, '_, '_>) {
157160
});
158161
}
159162

163+
// Implements the `etch` cheatcode for PVM.
164+
fn etch_call(target: &Address, new_runtime_code: &Bytes, ecx: Ecx<'_, '_, '_>) -> Result {
165+
let origin_address = H160::from_slice(ecx.tx.caller.as_slice());
166+
let origin_account = AccountId::to_fallback_account_id(&origin_address);
167+
168+
execute_with_externalities(|externalities| {
169+
externalities.execute_with(|| {
170+
let contract_blob = Pallet::<Runtime>::try_upload_pvm_code(
171+
origin_account.clone(),
172+
new_runtime_code.to_vec(),
173+
BalanceOf::<Runtime>::max_value(),
174+
&ExecConfig::new_substrate_tx(),
175+
)
176+
.map_err(|_| <&str as Into<Error>>::into("Could not upload PVM code"))?
177+
.0;
178+
let mut contract_info = if let Some(contract_info) =
179+
AccountInfo::<Runtime>::load_contract(&H160::from_slice(target.as_slice()))
180+
{
181+
contract_info
182+
} else {
183+
ContractInfo::<Runtime>::new(
184+
&origin_address,
185+
System::account_nonce(origin_account),
186+
*contract_blob.code_hash(),
187+
)
188+
.map_err(|_| <&str as Into<Error>>::into("Could not create contract info"))?
189+
};
190+
contract_info.code_hash = *contract_blob.code_hash();
191+
AccountInfo::<Runtime>::insert_contract(
192+
&H160::from_slice(target.as_slice()),
193+
contract_info,
194+
);
195+
Ok::<(), Error>(())
196+
})
197+
})?;
198+
Ok(Default::default())
199+
}
200+
160201
fn set_timestamp(new_timestamp: U256, ecx: Ecx<'_, '_, '_>) {
161202
// Set timestamp in EVM context.
162203
ecx.block.timestamp = new_timestamp;
@@ -251,6 +292,12 @@ impl CheatcodeInspectorStrategyRunner for PvmCheatcodeInspectorStrategyRunner {
251292

252293
Ok(Default::default())
253294
}
295+
t if using_pvm && is::<etchCall>(t) => {
296+
let &etchCall { ref target, ref newRuntimeBytecode } =
297+
cheatcode.as_any().downcast_ref().unwrap();
298+
etch_call(target, newRuntimeBytecode, ccx.ecx)?;
299+
cheatcode.dyn_apply(ccx, executor)
300+
}
254301
// Not custom, just invoke the default behavior
255302
_ => cheatcode.dyn_apply(ccx, executor),
256303
}

0 commit comments

Comments
 (0)