Skip to content

Commit 1d2353f

Browse files
pgherveougithub-actions[bot]athei
committed
[pallet-revive] tracing improvements (#7614)
Various pallet-revive improvements - add check for precompiles addresses, So we can easily identify which one are being called and not supported yet - fixes debug_call for revert call If a call revert we still want to get the traces for that call, that matches geth behaviors, diff tests will be added to the test suite for this - fixes traces for staticcall The call type was not always being reported properly. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Alexander Theißen <[email protected]>
1 parent 30f6d61 commit 1d2353f

File tree

10 files changed

+126
-24
lines changed

10 files changed

+126
-24
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,11 +2368,15 @@ impl_runtime_apis! {
23682368
{
23692369
use pallet_revive::tracing::trace;
23702370
let mut tracer = config.build(Revive::evm_gas_from_weight);
2371-
trace(&mut tracer, || {
2372-
Self::eth_transact(tx)
2373-
})?;
2374-
2375-
Ok(tracer.collect_traces().pop().expect("eth_transact succeeded, trace must exist, qed"))
2371+
let result = trace(&mut tracer, || Self::eth_transact(tx));
2372+
2373+
if let Some(trace) = tracer.collect_traces().pop() {
2374+
Ok(trace)
2375+
} else if let Err(err) = result {
2376+
Err(err)
2377+
} else {
2378+
Ok(Default::default())
2379+
}
23762380
}
23772381
}
23782382
}

prdoc/pr_7614.prdoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
title: '[pallet-revive] tracing improvements'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
Various pallet-revive improvements
6+
7+
- add check for precompiles addresses,
8+
So we can easily identified which one are being called and not supported yet
9+
10+
- fixes debug_call for revert call
11+
If a call revert we still want to get the traces for that call, that matches geth behaviors, diff tests will be added to the test suite for this
12+
13+
- fixes traces for staticcall
14+
The call type was not always being reported properly.
15+
crates:
16+
- name: asset-hub-westend-runtime
17+
bump: minor
18+
- name: pallet-revive-eth-rpc
19+
bump: minor
20+
- name: pallet-revive
21+
bump: minor

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3744,11 +3744,15 @@ impl_runtime_apis! {
37443744
{
37453745
use pallet_revive::tracing::trace;
37463746
let mut tracer = config.build(Revive::evm_gas_from_weight);
3747-
trace(&mut tracer, || {
3748-
Self::eth_transact(tx)
3749-
})?;
3750-
3751-
Ok(tracer.collect_traces().pop().expect("eth_transact succeeded, trace must exist, qed"))
3747+
let result = trace(&mut tracer, || Self::eth_transact(tx));
3748+
3749+
if let Some(trace) = tracer.collect_traces().pop() {
3750+
Ok(trace)
3751+
} else if let Err(err) = result {
3752+
Err(err)
3753+
} else {
3754+
Ok(Default::default())
3755+
}
37523756
}
37533757
}
37543758

-304 KB
Binary file not shown.

substrate/frame/revive/src/evm/api/debug_rpc_types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ pub struct CallTrace<Gas = U256> {
181181
#[serde(skip_serializing_if = "Vec::is_empty")]
182182
pub logs: Vec<CallLog>,
183183
/// Amount of value transferred.
184-
pub value: U256,
184+
#[serde(skip_serializing_if = "Option::is_none")]
185+
pub value: Option<U256>,
185186
/// Type of call.
186187
#[serde(rename = "type")]
187188
pub call_type: CallType,

substrate/frame/revive/src/evm/tracing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracer for CallTracer<Gas, GasM
7070
self.traces.push(CallTrace {
7171
from,
7272
to,
73-
value,
73+
value: if is_read_only { None } else { Some(value) },
7474
call_type,
7575
input: input.to_vec().into(),
7676
gas: (self.gas_mapper)(gas_left),

substrate/frame/revive/src/exec.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,13 @@ where
13731373
}
13741374
}
13751375

1376+
/// Determine if the given address is a precompile.
1377+
/// For now, we consider that all addresses between 0x1 and 0xff are reserved for precompiles.
1378+
fn is_precompile(address: &H160) -> bool {
1379+
let bytes = address.as_bytes();
1380+
bytes.starts_with(&[0u8; 19]) && bytes[19] != 0
1381+
}
1382+
13761383
impl<'a, T, E> Ext for Stack<'a, T, E>
13771384
where
13781385
T: Config,
@@ -1403,6 +1410,11 @@ where
14031410
*self.last_frame_output_mut() = Default::default();
14041411

14051412
let try_call = || {
1413+
if is_precompile(dest_addr) {
1414+
log::debug!(target: crate::LOG_TARGET, "Unsupported precompile address {dest_addr:?}");
1415+
return Err(Error::<T>::UnsupportedPrecompileAddress.into());
1416+
}
1417+
14061418
let dest = T::AddressMapper::to_account_id(dest_addr);
14071419
if !self.allows_reentry(&dest) {
14081420
return Err(<Error<T>>::ReentranceDenied.into());
@@ -1420,28 +1432,38 @@ where
14201432
CachedContract::Cached(contract) => Some(contract.clone()),
14211433
_ => None,
14221434
});
1435+
1436+
// Enable read-only access if requested; cannot disable it if already set.
1437+
let is_read_only = read_only || self.is_read_only();
1438+
14231439
if let Some(executable) = self.push_frame(
14241440
FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None },
14251441
value,
14261442
gas_limit,
14271443
deposit_limit.saturated_into::<BalanceOf<T>>(),
1428-
// Enable read-only access if requested; cannot disable it if already set.
1429-
read_only || self.is_read_only(),
1444+
is_read_only,
14301445
)? {
14311446
self.run(executable, input_data)
14321447
} else {
1433-
let result = Self::transfer_from_origin(
1434-
&self.origin,
1435-
&Origin::from_account_id(self.account_id().clone()),
1436-
&dest,
1437-
value,
1438-
);
1448+
let result = if is_read_only && value.is_zero() {
1449+
Ok(Default::default())
1450+
} else if is_read_only {
1451+
Err(Error::<T>::StateChangeDenied.into())
1452+
} else {
1453+
Self::transfer_from_origin(
1454+
&self.origin,
1455+
&Origin::from_account_id(self.account_id().clone()),
1456+
&dest,
1457+
value,
1458+
)
1459+
};
1460+
14391461
if_tracing(|t| {
14401462
t.enter_child_span(
14411463
T::AddressMapper::to_address(self.account_id()),
14421464
T::AddressMapper::to_address(&dest),
14431465
false,
1444-
false,
1466+
is_read_only,
14451467
value,
14461468
&input_data,
14471469
Weight::zero(),

substrate/frame/revive/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ pub mod pallet {
492492
InvalidGenericTransaction,
493493
/// The refcount of a code either over or underflowed.
494494
RefcountOverOrUnderflow,
495+
/// Unsupported precompile address
496+
UnsupportedPrecompileAddress,
495497
}
496498

497499
/// A reason for the pallet contracts placing a hold on funds.

substrate/frame/revive/src/test_utils/builder.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,10 @@ builder!(
146146

147147
/// Build the instantiate call and unwrap the account id.
148148
pub fn build_and_unwrap_contract(self) -> Contract<T> {
149-
let addr = self.build().result.unwrap().addr;
149+
let result = self.build().result.unwrap();
150+
assert!(!result.result.did_revert(), "instantiation did revert");
151+
152+
let addr = result.addr;
150153
let account_id = T::AddressMapper::to_account_id(&addr);
151154
Contract{ account_id, addr }
152155
}

substrate/frame/revive/src/tests.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4441,7 +4441,7 @@ fn tracing_works_for_transfers() {
44414441
vec![CallTrace {
44424442
from: ALICE_ADDR,
44434443
to: BOB_ADDR,
4444-
value: U256::from(10_000_000),
4444+
value: Some(U256::from(10_000_000)),
44454445
call_type: CallType::Call,
44464446
..Default::default()
44474447
},]
@@ -4509,6 +4509,7 @@ fn tracing_works() {
45094509
input: (3u32, addr_callee).encode().into(),
45104510
call_type: Call,
45114511
logs: logs.clone(),
4512+
value: Some(U256::from(0)),
45124513
calls: vec![
45134514
CallTrace {
45144515
from: addr,
@@ -4520,6 +4521,7 @@ fn tracing_works() {
45204521
revert_reason: Some("revert: This function always fails".to_string()),
45214522
error: Some("execution reverted".to_string()),
45224523
call_type: Call,
4524+
value: Some(U256::from(0)),
45234525
..Default::default()
45244526
},
45254527
CallTrace {
@@ -4528,6 +4530,7 @@ fn tracing_works() {
45284530
input: (2u32, addr_callee).encode().into(),
45294531
call_type: Call,
45304532
logs: logs.clone(),
4533+
value: Some(U256::from(0)),
45314534
calls: vec![
45324535
CallTrace {
45334536
from: addr,
@@ -4536,6 +4539,7 @@ fn tracing_works() {
45364539
output: Default::default(),
45374540
error: Some("ContractTrapped".to_string()),
45384541
call_type: Call,
4542+
value: Some(U256::from(0)),
45394543
..Default::default()
45404544
},
45414545
CallTrace {
@@ -4544,25 +4548,28 @@ fn tracing_works() {
45444548
input: (1u32, addr_callee).encode().into(),
45454549
call_type: Call,
45464550
logs: logs.clone(),
4551+
value: Some(U256::from(0)),
45474552
calls: vec![
45484553
CallTrace {
45494554
from: addr,
45504555
to: addr_callee,
45514556
input: 0u32.encode().into(),
45524557
output: 0u32.to_le_bytes().to_vec().into(),
45534558
call_type: Call,
4559+
value: Some(U256::from(0)),
45544560
..Default::default()
45554561
},
45564562
CallTrace {
45574563
from: addr,
45584564
to: addr,
45594565
input: (0u32, addr_callee).encode().into(),
45604566
call_type: Call,
4567+
value: Some(U256::from(0)),
45614568
calls: vec![
45624569
CallTrace {
45634570
from: addr,
45644571
to: BOB_ADDR,
4565-
value: U256::from(100),
4572+
value: Some(U256::from(100)),
45664573
call_type: CallType::Call,
45674574
..Default::default()
45684575
}
@@ -4582,3 +4589,41 @@ fn tracing_works() {
45824589
}
45834590
});
45844591
}
4592+
4593+
#[test]
4594+
fn unknown_precompiles_revert() {
4595+
let (code, _code_hash) = compile_module("read_only_call").unwrap();
4596+
4597+
ExtBuilder::default().build().execute_with(|| {
4598+
let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000_000);
4599+
let Contract { addr, .. } =
4600+
builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();
4601+
4602+
let cases: Vec<(H160, Box<dyn FnOnce(_)>)> = vec![
4603+
(
4604+
H160::from_low_u64_be(0x1),
4605+
Box::new(|result| {
4606+
assert_err!(result, <Error<Test>>::UnsupportedPrecompileAddress);
4607+
}),
4608+
),
4609+
(
4610+
H160::from_low_u64_be(0xff),
4611+
Box::new(|result| {
4612+
assert_err!(result, <Error<Test>>::UnsupportedPrecompileAddress);
4613+
}),
4614+
),
4615+
(
4616+
H160::from_low_u64_be(0x1ff),
4617+
Box::new(|result| {
4618+
assert_ok!(result);
4619+
}),
4620+
),
4621+
];
4622+
4623+
for (callee_addr, assert_result) in cases {
4624+
let result =
4625+
builder::bare_call(addr).data((callee_addr, [0u8; 0]).encode()).build().result;
4626+
assert_result(result);
4627+
}
4628+
});
4629+
}

0 commit comments

Comments
 (0)