Skip to content

Commit 40cd38d

Browse files
pgherveougithub-actions[bot]atheixermicus0xRVE
authored
[revive] revm backend (#9285)
# EVM initial support for pallet-revive Initial EVM support via the REVM crate to create a dual-VM system that can execute both PolkaVM and EVM - Added `AllowEVMBytecode: Get<bool>` to the config to enable/disable EVM call and instantiation - CodeInfo has been updated to add the type of bytecode (EVM / PVM). `migration/v2.rs` takes care of migrating existing storages - The CodeUploadDeposit is not held by a pallet account instead of being held on the uploader, It's automatically refunded when the refcount drops to 0 and the code is removed. - The basic flow of uploading an EVM contract and running it should work - instructions are copied and adapted from REVM they should be ignored in this PR and reviewed in follow-up PR (**reviewers** please ignore `substrate/frame/revive/src/vm/evm/instructions/*` for now) ## Implementation Guidelines ### Basic Instruction Structure A basic instruction looks like this: ```rust pub fn coinbase<'ext, E: Ext>(context: Context<'_, 'ext, E>) { gas_legacy!(context.interpreter, revm_gas::BASE); push!(context.interpreter, context.host.beneficiary().into_word().into()); } ``` ### Required Changes for REVM Instructions All instructions have been copied from `REVM` and updated with generic types for pallet-revive. Two main changes are required: #### 1. Gas Handling Replace REVM gas calls with existing benchmarks where available: ```diff - gas_legacy!(context.interpreter, revm_gas::BASE); + gas!(context.interpreter, RuntimeCosts::BlockAuthor); ``` #### 2. Context Access Replace `context.host` calls with `context.extend` (set to `&mut Ext`): ```diff - push!(context.interpreter, context.host.beneficiary().into_word().into()); + let coinbase: Address = context.interpreter.extend.block_author().unwrap_or_default().0.into(); + push!(context.interpreter, coinbase.into_word().into()); ``` ### Gas Benchmarking Notes - For cases without existing benchmarks (e.g arithmetic, bitwise) , we will keep `gas_legacy!` - The u64 gas value are multiplied by a base cost benchmarked by `evm_opcode` - ### Important Rules - All calls to `context.host` should be removed (initialized to default values) - All calls to `context.interpreter.gas` should be removed (except `gas.memory` handled by `resize_memory!` macro) - See `block_number` implementation as a reference example The following instructions in src/vm/evm/instructions/** need to be updated ### Basic Instructions We probably don't need to touch these implementations here, they use the gas_legacy! macro to charge a low gas value that will be scaled with our gas_to_weight benchmark. The only thing needed here are tests that exercise these instructions <details> #### Arithmetic Instructions - [ ] **add** - [ ] **mul** - [ ] **sub** - [ ] **div** - [ ] **sdiv** - [ ] **rem** - [ ] **smod** - [ ] **addmod** - [ ] **mulmod** - [ ] **exp** - [ ] **signextend** #### Bitwise Instructions - [ ] **lt** - [ ] **gt** - [ ] **slt** - [ ] **sgt** - [ ] **eq** - [ ] **iszero** - [ ] **bitand** - [ ] **bitor** - [ ] **bitxor** - [ ] **not** - [ ] **byte** - [ ] **shl** - [ ] **shr** - [ ] **sar** - [ ] **clz** #### Control Flow Instructions - [ ] **jump** - [ ] **jumpi** - [ ] **jumpdest** - [ ] **pc** - [ ] **stop** - [ ] **ret** - [ ] **revert** - [ ] **invalid** ### Memory Instructions - [ ] **mload** - [ ] **mstore** - [ ] **mstore8** - [ ] **msize** - [ ] **mcopy** #### Stack Instructions - [ ] **pop** - [ ] **push0** - [ ] **push** - [ ] **dup** - [ ] **swap** </details> ### Sys calls instructions These instructions should be updated from using gas_legacy! to gas! with the appropriate RuntimeCost, the returned value need to be pulled from our `&mut Ext` ctx.interpreter.extend instead of the host or input context value <details> #### Block Info Instructions - [x] **block_number** - [ ] **coinbase** - [ ] **timestamp** - [ ] **difficulty** - [ ] **gaslimit** - [ ] **chainid** - [ ] **basefee** - [ ] **blob_basefee** #### Host Instructions - [ ] **balance** - [ ] **extcodesize** - [ ] **extcodecopy** - [ ] **extcodehash** - [ ] **blockhash** - [ ] **sload** - [ ] **sstore** - [ ] **tload** - [ ] **tstore** - [ ] **log** - [ ] **selfdestruct** - [ ] **selfbalance** #### System Instructions - [ ] **keccak256** - [ ] **address** - [ ] **caller** - [ ] **callvalue** - [ ] **calldataload** - [ ] **calldatasize** - [ ] **calldatacopy** - [ ] **codesize** - [ ] **codecopy** - [ ] **returndatasize** - [ ] **returndatacopy** - [ ] **gas** #### Transaction Info Instructions - [ ] **origin** - [ ] **gasprice** - [ ] **blob_hash** </details> ### Contract Instructions These instructions should be updated,, that's where I expect the most code change in the instruction implementation. See how it's done in vm/pvm module, the final result should look pretty similar to what we are doing there with the addition of custom gas_limit calculation that works with our gas model. see also example code here https://github.com/paritytech/revm_example <details> - [ ] **create** - [ ] **create** - [ ] **call** - [ ] **call_code** - [ ] **delegate_call** - [ ] **static_call** </details> --------- Signed-off-by: Cyrill Leutwiler <[email protected]> Signed-off-by: xermicus <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Alexander Theißen <[email protected]> Co-authored-by: xermicus <[email protected]> Co-authored-by: 0xRVE <[email protected]> Co-authored-by: Robert van Eerdewijk <[email protected]>
1 parent 3381910 commit 40cd38d

File tree

45 files changed

+5136
-886
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5136
-886
lines changed

.github/workflows/tests-misc.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,17 @@ jobs:
370370
components: cargo, clippy, rust-docs, rust-src, rustfmt, rustc, rust-std
371371
- name: Install protobuf
372372
run: brew install protobuf
373+
- name: install solc
374+
run: brew install solidity
375+
- name: Install resolc
376+
run: |
377+
VERSION="0.3.0"
378+
ASSET_URL="https://github.com/paritytech/revive/releases/download/v$VERSION/resolc-universal-apple-darwin"
379+
echo "Downloading resolc v$VERSION from $ASSET_URL"
380+
curl -Lsf --show-error -o $HOME/.cargo/bin/resolc "$ASSET_URL"
381+
chmod +x $HOME/.cargo/bin/resolc
382+
xattr -c $HOME/.cargo/bin/resolc
383+
resolc --version
373384
- name: cargo info
374385
run: |
375386
echo "######## rustup show ########"
@@ -406,3 +417,4 @@ jobs:
406417
else
407418
echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY
408419
fi
420+

cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,10 @@ impl pallet_revive::Config for Runtime {
11821182
type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
11831183
type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
11841184
type UnsafeUnstableInterface = ConstBool<false>;
1185+
#[cfg(feature = "runtime-benchmarks")]
1186+
type AllowEVMBytecode = ConstBool<true>;
1187+
#[cfg(not(feature = "runtime-benchmarks"))]
1188+
type AllowEVMBytecode = ConstBool<false>;
11851189
type UploadOrigin = EnsureSigned<Self::AccountId>;
11861190
type InstantiateOrigin = EnsureSigned<Self::AccountId>;
11871191
type RuntimeHoldReason = RuntimeHoldReason;
@@ -1199,7 +1203,10 @@ parameter_types! {
11991203
impl pallet_migrations::Config for Runtime {
12001204
type RuntimeEvent = RuntimeEvent;
12011205
#[cfg(not(feature = "runtime-benchmarks"))]
1202-
type Migrations = pallet_revive::migrations::v1::Migration<Runtime>;
1206+
type Migrations = (
1207+
pallet_revive::migrations::v1::Migration<Runtime>,
1208+
pallet_revive::migrations::v2::Migration<Runtime>,
1209+
);
12031210
// Benchmarks need mocked migrations to guarantee that they succeed.
12041211
#[cfg(feature = "runtime-benchmarks")]
12051212
type Migrations = pallet_migrations::mock_helpers::MockedMigrations;

cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,11 +1664,14 @@ fn weight_of_message_increases_when_dealing_with_erc20s() {
16641664
fn withdraw_and_deposit_erc20s() {
16651665
let sender: AccountId = ALICE.into();
16661666
let beneficiary: AccountId = BOB.into();
1667+
let revive_account = pallet_revive::Pallet::<Runtime>::account_id();
16671668
let checking_account =
16681669
asset_hub_westend_runtime::xcm_config::ERC20TransfersCheckingAccount::get();
16691670
let initial_wnd_amount = 10_000_000_000_000u128;
16701671

16711672
ExtBuilder::<Runtime>::default().build().execute_with(|| {
1673+
// Bring the revive account to life.
1674+
assert_ok!(Balances::mint_into(&revive_account, initial_wnd_amount));
16721675
// We need to give enough funds for every account involved so they
16731676
// can call `Revive::map_account`.
16741677
assert_ok!(Balances::mint_into(&sender, initial_wnd_amount));
@@ -1729,13 +1732,16 @@ fn withdraw_and_deposit_erc20s() {
17291732
fn non_existent_erc20_will_error() {
17301733
let sender: AccountId = ALICE.into();
17311734
let beneficiary: AccountId = BOB.into();
1735+
let revive_account = pallet_revive::Pallet::<Runtime>::account_id();
17321736
let checking_account =
17331737
asset_hub_westend_runtime::xcm_config::ERC20TransfersCheckingAccount::get();
17341738
let initial_wnd_amount = 10_000_000_000_000u128;
17351739
// We try to withdraw an ERC20 token but the address doesn't exist.
17361740
let non_existent_contract_address = [1u8; 20];
17371741

17381742
ExtBuilder::<Runtime>::default().build().execute_with(|| {
1743+
// Bring the revive account to life.
1744+
assert_ok!(Balances::mint_into(&revive_account, initial_wnd_amount));
17391745
// We need to give enough funds for every account involved so they
17401746
// can call `Revive::map_account`.
17411747
assert_ok!(Balances::mint_into(&sender, initial_wnd_amount));
@@ -1772,11 +1778,15 @@ fn non_existent_erc20_will_error() {
17721778
fn smart_contract_not_erc20_will_error() {
17731779
let sender: AccountId = ALICE.into();
17741780
let beneficiary: AccountId = BOB.into();
1781+
let revive_account = pallet_revive::Pallet::<Runtime>::account_id();
17751782
let checking_account =
17761783
asset_hub_westend_runtime::xcm_config::ERC20TransfersCheckingAccount::get();
17771784
let initial_wnd_amount = 10_000_000_000_000u128;
17781785

17791786
ExtBuilder::<Runtime>::default().build().execute_with(|| {
1787+
// Bring the revive account to life.
1788+
assert_ok!(Balances::mint_into(&revive_account, initial_wnd_amount));
1789+
17801790
// We need to give enough funds for every account involved so they
17811791
// can call `Revive::map_account`.
17821792
assert_ok!(Balances::mint_into(&sender, initial_wnd_amount));
@@ -1822,11 +1832,15 @@ fn smart_contract_not_erc20_will_error() {
18221832
fn smart_contract_does_not_return_bool_fails() {
18231833
let sender: AccountId = ALICE.into();
18241834
let beneficiary: AccountId = BOB.into();
1835+
let revive_account = pallet_revive::Pallet::<Runtime>::account_id();
18251836
let checking_account =
18261837
asset_hub_westend_runtime::xcm_config::ERC20TransfersCheckingAccount::get();
18271838
let initial_wnd_amount = 10_000_000_000_000u128;
18281839

18291840
ExtBuilder::<Runtime>::default().build().execute_with(|| {
1841+
// Bring the revive account to life.
1842+
assert_ok!(Balances::mint_into(&revive_account, initial_wnd_amount));
1843+
18301844
// We need to give enough funds for every account involved so they
18311845
// can call `Revive::map_account`.
18321846
assert_ok!(Balances::mint_into(&sender, initial_wnd_amount));
@@ -1875,11 +1889,15 @@ fn smart_contract_does_not_return_bool_fails() {
18751889
fn expensive_erc20_runs_out_of_gas() {
18761890
let sender: AccountId = ALICE.into();
18771891
let beneficiary: AccountId = BOB.into();
1892+
let revive_account = pallet_revive::Pallet::<Runtime>::account_id();
18781893
let checking_account =
18791894
asset_hub_westend_runtime::xcm_config::ERC20TransfersCheckingAccount::get();
18801895
let initial_wnd_amount = 10_000_000_000_000u128;
18811896

18821897
ExtBuilder::<Runtime>::default().build().execute_with(|| {
1898+
// Bring the revive account to life.
1899+
assert_ok!(Balances::mint_into(&revive_account, initial_wnd_amount));
1900+
18831901
// We need to give enough funds for every account involved so they
18841902
// can call `Revive::map_account`.
18851903
assert_ok!(Balances::mint_into(&sender, initial_wnd_amount));

cumulus/parachains/runtimes/testing/penpal/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ impl pallet_revive::Config for Runtime {
853853
type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
854854
type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
855855
type UnsafeUnstableInterface = ConstBool<true>;
856+
type AllowEVMBytecode = ConstBool<true>;
856857
type UploadOrigin = EnsureSigned<Self::AccountId>;
857858
type InstantiateOrigin = EnsureSigned<Self::AccountId>;
858859
type RuntimeHoldReason = RuntimeHoldReason;

prdoc/pr_9285.prdoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
title: '[revive] revm backend'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |
5+
Initial EVM support for pallet-revive via the REVM crate to create a dual-VM system that can execute both PolkaVM and EVM
6+
- Added AllowEVMBytecode: Get<bool> to the config to enable/disable EVM call and instantiation
7+
- The basic flow of uploading an EVM contract and running it should work
8+
- instructions are copied and adapted from REVM they should be ignored in this PR and reviewed in follow-up PR
9+
crates:
10+
- name: pallet-revive
11+
bump: major
12+
- name: pallet-revive-fixtures
13+
bump: patch
14+
- name: asset-hub-westend-runtime
15+
bump: patch
16+
- name: penpal-runtime
17+
bump: patch

substrate/bin/node/runtime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,7 @@ impl pallet_revive::Config for Runtime {
14901490
type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12.
14911491
type EthGasEncoder = ();
14921492
type FindAuthor = <Runtime as pallet_authorship::Config>::FindAuthor;
1493+
type AllowEVMBytecode = ConstBool<true>;
14931494
}
14941495

14951496
impl pallet_sudo::Config for Runtime {

substrate/frame/revive/fixtures/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ fn generate_fixture_location(temp_dir: &Path, out_dir: &Path, entries: &[Entry])
463463
// Generate sol! macros for Solidity contracts
464464
for entry in entries.iter().filter(|e| matches!(e.contract_type, ContractType::Solidity)) {
465465
let relative_path = format!("contracts/{}", entry.path().split('/').last().unwrap());
466-
writeln!(file, r#"alloy_core::sol!("{}");"#, relative_path)
466+
writeln!(file, r#"#[cfg(feature = "std")] alloy_core::sol!("{}");"#, relative_path)
467467
.context("Failed to write sol! macro to fixture_location.rs")?;
468468
}
469469

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
contract Fibonacci {
5+
function fib(uint n) public pure returns (uint) {
6+
if (n <= 1) {
7+
return n;
8+
}
9+
return fib(n - 1) + fib(n - 2);
10+
}
11+
}

0 commit comments

Comments
 (0)