Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
630c1ad
refactor
pgherveou Sep 25, 2025
8d15995
rm comments
pgherveou Sep 25, 2025
dd361ed
wip
pgherveou Sep 25, 2025
d0efc26
fix
pgherveou Sep 25, 2025
3bcde5b
lint
pgherveou Sep 25, 2025
66db1b2
fix missing alloc import
pgherveou Sep 26, 2025
3015039
fix
pgherveou Sep 26, 2025
d52c3c7
Update substrate/frame/revive/src/vm/evm/instructions/arithmetic/modu…
pgherveou Sep 26, 2025
1eb8141
fixes
pgherveou Sep 26, 2025
fec8286
wip
pgherveou Sep 26, 2025
87517a9
just use safe code
pgherveou Sep 26, 2025
7331790
nit
pgherveou Sep 26, 2025
d745f06
simplify tests
pgherveou Sep 26, 2025
8b7d87f
PR reviews
pgherveou Sep 26, 2025
2f82899
fix
pgherveou Sep 26, 2025
fb7da96
pr review
pgherveou Sep 26, 2025
d93dd78
rm exra copytocontract
pgherveou Sep 26, 2025
a1c29aa
fix
pgherveou Sep 26, 2025
e2c0230
pr review
pgherveou Sep 26, 2025
3f73bc1
pr review
pgherveou Sep 26, 2025
a6a3293
PR review
pgherveou Sep 26, 2025
15542fa
resize
pgherveou Sep 26, 2025
4c34be1
fix
pgherveou Sep 27, 2025
b5ddc9a
set capacity
pgherveou Sep 27, 2025
91f676f
nit
pgherveou Sep 27, 2025
9ca0ac9
fix
pgherveou Sep 27, 2025
9ea176c
clippy
pgherveou Sep 27, 2025
96d2a51
fix
pgherveou Sep 27, 2025
4ff8fe2
fix
pgherveou Sep 29, 2025
77c4871
share common error
pgherveou Sep 29, 2025
07f5c9f
fix
pgherveou Sep 29, 2025
a51ea32
Update substrate/frame/revive/src/vm/evm/instructions/system.rs
pgherveou Sep 29, 2025
600e5ac
Merge branch 'master' into pg/revm-refactor
pgherveou Sep 29, 2025
e8f42ac
fixes
pgherveou Sep 29, 2025
0424993
fix
pgherveou Sep 29, 2025
233c6cb
fix
pgherveou Sep 29, 2025
398c5af
nit
pgherveou Sep 29, 2025
c02a01d
fmt
pgherveou Sep 29, 2025
f8b88ec
nit
pgherveou Sep 29, 2025
262172f
Update from github-actions[bot] running command 'prdoc --audience run…
github-actions[bot] Sep 29, 2025
caed10e
nit
pgherveou Sep 29, 2025
a9d6ee9
rm log
pgherveou Sep 29, 2025
4d74dbe
nit
pgherveou Sep 29, 2025
05f57d1
simpler sol interface
pgherveou Sep 30, 2025
7f4bb09
clippy
pgherveou Sep 30, 2025
fd541ad
fmt prdoc
pgherveou Sep 30, 2025
ae05b84
fmt
pgherveou Sep 30, 2025
73d3f15
simplify tests
pgherveou Sep 30, 2025
389e749
fmt with forge fmt
pgherveou Sep 30, 2025
01c7d46
nit
pgherveou Sep 30, 2025
2c0964a
add back removed test
pgherveou Sep 30, 2025
48105db
Merge branch 'master' into pg/revm-refactor
pgherveou Oct 1, 2025
56a0c34
fix
pgherveou Oct 1, 2025
7bf26db
Rve/9565 pallet revive evm backend ensure memory dos safety2 (#9887)
0xRVE Oct 2, 2025
8d6b69b
Update substrate/frame/revive/src/limits.rs
pgherveou Oct 2, 2025
4e24f59
Update substrate/frame/revive/src/limits.rs
pgherveou Oct 2, 2025
6329694
cleanup unsafe & unsued traits impl
pgherveou Oct 3, 2025
765733d
nit
pgherveou Oct 3, 2025
efd0512
Merge branch 'master' into pg/revm-refactor
pgherveou Oct 3, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,7 @@ procfs = { version = "0.16.0" }
prometheus = { version = "0.13.0", default-features = false }
prometheus-endpoint = { path = "substrate/utils/prometheus", default-features = false, package = "substrate-prometheus-endpoint" }
prometheus-parse = { version = "0.2.2" }
proptest = { version = "1" }
prost = { version = "0.12.4" }
prost-build = { version = "0.13.2" }
pyroscope = { version = "0.5.8" }
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/revive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pallet-proxy = { workspace = true, default-features = true }
pallet-revive-fixtures = { workspace = true, default-features = true }
pallet-timestamp = { workspace = true, default-features = true }
pallet-utility = { workspace = true, default-features = true }
proptest = { workspace = true }
sp-keystore = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }

Expand Down
44 changes: 11 additions & 33 deletions substrate/frame/revive/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
storage::WriteOutcome,
vm::{
evm,
evm::{instructions::instruction_table, EVMInterpreter},
evm::{instructions::host, Interpreter},
pvm,
},
Pallet as Contracts, *,
Expand All @@ -47,13 +47,7 @@ use frame_support::{
};
use frame_system::RawOrigin;
use pallet_revive_uapi::{pack_hi_lo, CallFlags, ReturnErrorCode, StorageFlags};
use revm::{
bytecode::{opcode::EXTCODECOPY, Bytecode},
interpreter::{
host::DummyHost, interpreter_types::MemoryTr, InstructionContext, Interpreter, SharedMemory,
},
primitives,
};
use revm::bytecode::Bytecode;
use sp_consensus_aura::AURA_ENGINE_ID;
use sp_consensus_babe::{
digests::{PreDigest, PrimaryPreDigest},
Expand Down Expand Up @@ -2307,7 +2301,7 @@ mod benchmarks {
#[benchmark(pov_mode = Measured)]
fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
let module = VmBinaryModule::evm_noop(r);
let inputs = evm::EVMInputs::new(vec![]);
let inputs = vec![];

let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
let mut setup = CallSetup::<T>::new(module);
Expand Down Expand Up @@ -2412,38 +2406,22 @@ mod benchmarks {
let mut setup = CallSetup::<T>::new(module);
let contract = setup.contract();

let mut address: [u8; 32] = [0; 32];
address[12..].copy_from_slice(&contract.address.0);

let (mut ext, _) = setup.ext();
let mut interpreter: Interpreter<EVMInterpreter<'_, _>> = Interpreter {
extend: &mut ext,
input: Default::default(),
bytecode: Default::default(),
gas: Default::default(),
stack: Default::default(),
return_data: Default::default(),
memory: SharedMemory::new(),
runtime_flag: Default::default(),
};

let table = instruction_table::<'_, _>();
let extcodecopy_fn = table[EXTCODECOPY as usize];
let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);

// Setup stack for extcodecopy instruction: [address, dest_offset, offset, size]
let _ = interpreter.stack.push(primitives::U256::from(n));
let _ = interpreter.stack.push(primitives::U256::from(0u32));
let _ = interpreter.stack.push(primitives::U256::from(0u32));
let _ = interpreter.stack.push(primitives::U256::from_be_bytes(address));

let mut host = DummyHost {};
let context = InstructionContext { interpreter: &mut interpreter, host: &mut host };
let _ = interpreter.stack.push(U256::from(n));
let _ = interpreter.stack.push(U256::from(0u32));
let _ = interpreter.stack.push(U256::from(0u32));
let _ = interpreter.stack.push(contract.address);

let result;
#[block]
{
extcodecopy_fn(context);
result = host::extcodecopy(&mut interpreter);
}

assert!(result.is_continue());
assert_eq!(
*interpreter.memory.slice(0..n as usize),
PristineCode::<T>::get(contract.info()?.code_hash).unwrap()[0..n as usize],
Expand Down
10 changes: 9 additions & 1 deletion substrate/frame/revive/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
Pallet as Contracts, RuntimeCosts, LOG_TARGET,
};
use alloc::vec::Vec;
use core::{fmt::Debug, marker::PhantomData, mem};
use core::{fmt::Debug, marker::PhantomData, mem, ops::ControlFlow};
use frame_support::{
crypto::ecdsa::ECDSAExt,
dispatch::DispatchResult,
Expand Down Expand Up @@ -287,6 +287,14 @@ pub trait PrecompileExt: sealing::Sealed {
.adjust_gas(charged, RuntimeCosts::Precompile(actual_weight));
}

/// Charges the gas meter with the given token or halts execution if not enough gas is left.
fn charge_or_halt<Tok: crate::gas::Token<Self::T>>(
&mut self,
token: Tok,
) -> ControlFlow<crate::vm::evm::Halt, crate::gas::ChargedAmount> {
self.gas_meter_mut().charge_or_halt(token)
}
Comment on lines +293 to +299
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Isn't charge already supposed to stop execution when not enough gas? Can't the ControlFlow be created by the caller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is similar just a convenient fn to return a ControlFlow instead of a Result


/// Call (possibly transferring some amount of funds) into the specified account.
///
/// Returns the code size of the called contract.
Expand Down
23 changes: 10 additions & 13 deletions substrate/frame/revive/src/gas.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::vm::evm::HaltReason;
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
Expand All @@ -14,9 +15,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{exec::ExecError, weights::WeightInfo, Config, Error};
use core::marker::PhantomData;
use crate::{exec::ExecError, vm::evm::Halt, weights::WeightInfo, Config, Error};
use core::{marker::PhantomData, ops::ControlFlow};
use frame_support::{
dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo},
weights::Weight,
Expand Down Expand Up @@ -219,16 +219,13 @@ impl<T: Config> GasMeter<T> {
Ok(ChargedAmount(amount))
}

/// Charge the specified amount of EVM gas.
/// This is used for basic opcodes (e.g arithmetic, bitwise, ...) that don't have a dedicated
/// benchmark
pub fn charge_evm_gas(&mut self, gas: u64) -> Result<(), DispatchError> {
let base_cost = T::WeightInfo::evm_opcode(1).saturating_sub(T::WeightInfo::evm_opcode(0));
self.gas_left = self
.gas_left
.checked_sub(&base_cost.saturating_mul(gas))
.ok_or_else(|| Error::<T>::OutOfGas)?;
Ok(())
/// Charge the specified token amount of gas or halt if not enough gas is left.
pub fn charge_or_halt<Tok: Token<T>>(
&mut self,
token: Tok,
) -> ControlFlow<Halt, ChargedAmount> {
self.charge(token)
.map_or_else(|_| ControlFlow::Break(HaltReason::OutOfGas.into()), ControlFlow::Continue)
}

/// Adjust a previously charged amount down to its actual amount.
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/revive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@ pub mod pallet {
CallDataTooLarge = 0x30,
/// The return data exceeds [`limits::CALLDATA_BYTES`].
ReturnDataTooLarge = 0x31,
/// EVM execution halted with the given reason.
Halt(crate::vm::evm::HaltReason) = 0x32,
}

/// A reason for the pallet revive placing a hold on funds.
Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/revive/src/precompiles/builtin/bn128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl<T: Config> PrimitivePrecompile for Bn128Pairing<T> {
input: Vec<u8>,
env: &mut impl Ext<T = Self::T>,
) -> Result<Vec<u8>, Error> {
if input.len() % 192 != 0 {
if !input.len().is_multiple_of(192) {
Err(DispatchError::from("invalid input length"))?;
}

Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/revive/src/precompiles/builtin/modexp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn calculate_gas_cost(
fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 {
let max_length = max(base_length, mod_length);
let mut words = max_length / 8;
if max_length % 8 > 0 {
if !max_length.is_multiple_of(8) {
words += 1;
}

Expand Down
34 changes: 17 additions & 17 deletions substrate/frame/revive/src/tests/sol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ use crate::{
test_utils::{contract_base_deposit, ensure_stored, get_contract},
ExtBuilder, Test,
},
Code, Config, PristineCode,
Code, Config, PristineCode, U256,
};
use alloy_core::{primitives::U256, sol_types::SolInterface};
use alloy_core::{primitives, sol_types::SolInterface};
use frame_support::traits::fungible::Mutate;
use pallet_revive_fixtures::{compile_module_with_type, Fibonacci, FixtureType};
use pretty_assertions::assert_eq;
Expand Down Expand Up @@ -88,14 +88,13 @@ fn basic_evm_flow_works() {

let result = builder::bare_call(addr)
.data(
Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall { n: U256::from(10u64) })
.abi_encode(),
Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall {
n: primitives::U256::from(10u64),
})
.abi_encode(),
)
.build_and_unwrap_result();
assert_eq!(
U256::from(55u32),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
);
assert_eq!(U256::from(55u32), U256::from_big_endian(&result.data));
}

// init code is not stored
Expand Down Expand Up @@ -140,26 +139,27 @@ fn basic_evm_flow_tracing_works() {
let result = trace(&mut call_tracer, || {
builder::bare_call(addr)
.data(
Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall { n: U256::from(10u64) })
.abi_encode(),
Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall {
n: primitives::U256::from(10u64),
})
.abi_encode(),
)
.build_and_unwrap_result()
});

assert_eq!(
U256::from(55u32),
U256::from_be_bytes::<32>(result.data.clone().try_into().unwrap())
);
assert_eq!(crate::U256::from(55u32), crate::U256::from_big_endian(&result.data));

assert_eq!(
call_tracer.collect_trace().unwrap(),
CallTrace {
call_type: CallType::Call,
from: ALICE_ADDR,
to: addr,
input: Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall { n: U256::from(10u64) })
.abi_encode()
.into(),
input: Fibonacci::FibonacciCalls::fib(Fibonacci::fibCall {
n: primitives::U256::from(10u64)
})
.abi_encode()
.into(),
output: result.data.into(),
value: Some(crate::U256::zero()),
..Default::default()
Expand Down
7 changes: 3 additions & 4 deletions substrate/frame/revive/src/tests/sol/bitwise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ fn bitwise_works() {
let result = builder::bare_call(addr)
.data(Bitwise::BitwiseCalls::testBitwise(Bitwise::testBitwiseCall {}).abi_encode())
.build_and_unwrap_result();

if result.did_revert() {
if let Some(revert_msg) = decode_revert_reason(&result.data) {
log::error!("Revert message: {}", revert_msg);
panic!("Revert message: {revert_msg}");
} else {
log::error!("Revert without message, raw data: {:?}", result.data);
panic!("Revert without message, raw data: {:?}", result.data);
}
}

assert!(!result.did_revert(), "bitwise test reverted");
});
}
}
24 changes: 9 additions & 15 deletions substrate/frame/revive/src/tests/sol/block_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
use crate::{
test_utils::{builder::Contract, ALICE},
tests::{builder, Contracts, ExtBuilder, System, Test, Timestamp},
vm::evm::{U256Converter, BASE_FEE, DIFFICULTY},
vm::evm::{BASE_FEE, DIFFICULTY},
Code, Config,
};

use alloy_core::{primitives::U256, sol_types::SolInterface};
use alloy_core::sol_types::SolInterface;
use frame_support::traits::fungible::Mutate;
use pallet_revive_fixtures::{compile_module_with_type, BlockInfo, FixtureType};
use pretty_assertions::assert_eq;
use sp_core::H160;
use sp_core::{H160, U256};

/// Tests that the blocknumber opcode works as expected.
#[test]
Expand All @@ -48,10 +48,7 @@ fn block_number_works() {
.abi_encode(),
)
.build_and_unwrap_result();
assert_eq!(
U256::from(42u32),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
);
assert_eq!(U256::from(42u32), U256::from_big_endian(&result.data));
});
}
}
Expand Down Expand Up @@ -93,7 +90,7 @@ fn chainid_works() {
.build_and_unwrap_result();
assert_eq!(
U256::from(<Test as Config>::ChainId::get()),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
U256::from_big_endian(&result.data)
);
});
}
Expand All @@ -119,7 +116,7 @@ fn timestamp_works() {
// Solidity expects timestamps in seconds, whereas pallet_timestamp uses
// milliseconds.
U256::from(Timestamp::get() / 1000),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
U256::from_big_endian(&result.data)
);
});
}
Expand All @@ -142,7 +139,7 @@ fn gaslimit_works() {
U256::from(
<Test as frame_system::Config>::BlockWeights::get().max_block.ref_time()
),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
U256::from_big_endian(&result.data)
);
});
}
Expand All @@ -161,10 +158,7 @@ fn basefee_works() {
let result = builder::bare_call(addr)
.data(BlockInfo::BlockInfoCalls::basefee(BlockInfo::basefeeCall {}).abi_encode())
.build_and_unwrap_result();
assert_eq!(
BASE_FEE.into_revm_u256(),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
);
assert_eq!(BASE_FEE, U256::from_big_endian(&result.data));
});
}
}
Expand All @@ -188,7 +182,7 @@ fn difficulty_works() {
assert_eq!(
// Alligned with the value set for PVM
U256::from(DIFFICULTY),
U256::from_be_bytes::<32>(result.data.try_into().unwrap())
U256::from_big_endian(&result.data)
);
});
}
Expand Down
Loading