Skip to content

Commit 1f8303a

Browse files
pgherveougithub-actions[bot]
authored andcommitted
[pallet-revive] fix call-trace create calls (#8781)
Fix call-traces for CREATE calls. these types of calls have a "CREATE" and "CREATE2" type see https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#call-tracer The input of these trace, should also contain the bytecode or code hash. fixes paritytech/contract-issues#96 --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 3ac8084 commit 1f8303a

File tree

10 files changed

+187
-18
lines changed

10 files changed

+187
-18
lines changed

prdoc/pr_8781.prdoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
title: '[pallet-revive] fix call-trace create calls'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
Fix call-traces for CREATE calls.
6+
7+
fixes https://github.com/paritytech/contract-issues/issues/96
8+
crates:
9+
- name: pallet-revive-fixtures
10+
bump: patch
11+
- name: pallet-revive
12+
bump: patch
13+
- name: pallet-revive-eth-rpc
14+
bump: patch
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
#![no_std]
19+
#![no_main]
20+
include!("../panic_handler.rs");
21+
22+
use uapi::{input, HostFn, HostFnImpl as api};
23+
24+
#[no_mangle]
25+
#[polkavm_derive::polkavm_export]
26+
pub extern "C" fn deploy() {}
27+
28+
#[no_mangle]
29+
#[polkavm_derive::polkavm_export]
30+
pub extern "C" fn call() {
31+
input!(128, code_hash: [u8],);
32+
33+
let mut value = [0; 32];
34+
api::value_transferred(&mut value);
35+
36+
// Deploy the contract with salt (equivalent to create2).
37+
let salt = [1u8; 32];
38+
api::instantiate(
39+
u64::MAX,
40+
u64::MAX,
41+
&[u8::MAX; 32],
42+
&value,
43+
code_hash,
44+
None,
45+
None,
46+
Some(&salt),
47+
)
48+
.unwrap();
49+
}
303 Bytes
Binary file not shown.

substrate/frame/revive/rpc/src/client/runtime_api.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,10 @@ impl RuntimeApi {
113113
transaction_index: u32,
114114
tracer_type: crate::TracerType,
115115
) -> Result<Trace, ClientError> {
116-
let payload = subxt_client::apis().revive_api().trace_tx(
117-
block.into(),
118-
transaction_index,
119-
tracer_type.into(),
120-
);
116+
let payload = subxt_client::apis()
117+
.revive_api()
118+
.trace_tx(block.into(), transaction_index, tracer_type.into())
119+
.unvalidated();
121120

122121
let trace = self.0.call(payload).await?.ok_or(ClientError::EthExtrinsicNotFound)?.0;
123122
Ok(trace)
@@ -132,8 +131,10 @@ impl RuntimeApi {
132131
>,
133132
tracer_type: crate::TracerType,
134133
) -> Result<Vec<(u32, Trace)>, ClientError> {
135-
let payload =
136-
subxt_client::apis().revive_api().trace_block(block.into(), tracer_type.into());
134+
let payload = subxt_client::apis()
135+
.revive_api()
136+
.trace_block(block.into(), tracer_type.into())
137+
.unvalidated();
137138

138139
let traces = self.0.call(payload).await?.into_iter().map(|(idx, t)| (idx, t.0)).collect();
139140
Ok(traces)
@@ -147,7 +148,8 @@ impl RuntimeApi {
147148
) -> Result<Trace, ClientError> {
148149
let payload = subxt_client::apis()
149150
.revive_api()
150-
.trace_call(transaction.into(), tracer_type.into());
151+
.trace_call(transaction.into(), tracer_type.into())
152+
.unvalidated();
151153

152154
let trace = self.0.call(payload).await?.map_err(|err| ClientError::TransactError(err.0))?;
153155
Ok(trace.0)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ pub enum CallType {
159159
StaticCall,
160160
/// A delegate call.
161161
DelegateCall,
162+
/// A create call.
163+
Create,
164+
/// A create2 call.
165+
Create2,
162166
}
163167

164168
/// A Trace

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

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
evm::{decode_revert_reason, CallLog, CallTrace, CallTracerConfig, CallType},
1919
primitives::ExecReturnValue,
2020
tracing::Tracing,
21-
DispatchError, Weight,
21+
Code, DispatchError, Weight,
2222
};
2323
use alloc::{format, string::ToString, vec::Vec};
2424
use sp_core::{H160, H256, U256};
@@ -32,14 +32,22 @@ pub struct CallTracer<Gas, GasMapper> {
3232
traces: Vec<CallTrace<Gas>>,
3333
/// Stack of indices to the current active traces.
3434
current_stack: Vec<usize>,
35+
/// The code and salt used to instantiate the next contract.
36+
code_with_salt: Option<(Code, bool)>,
3537
/// The tracer configuration.
3638
config: CallTracerConfig,
3739
}
3840

3941
impl<Gas, GasMapper> CallTracer<Gas, GasMapper> {
4042
/// Create a new [`CallTracer`] instance.
4143
pub fn new(config: CallTracerConfig, gas_mapper: GasMapper) -> Self {
42-
Self { gas_mapper, traces: Vec::new(), current_stack: Vec::new(), config }
44+
Self {
45+
gas_mapper,
46+
traces: Vec::new(),
47+
code_with_salt: None,
48+
current_stack: Vec::new(),
49+
config,
50+
}
4351
}
4452

4553
/// Collect the traces and return them.
@@ -49,6 +57,10 @@ impl<Gas, GasMapper> CallTracer<Gas, GasMapper> {
4957
}
5058

5159
impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracing for CallTracer<Gas, GasMapper> {
60+
fn instantiate_code(&mut self, code: &Code, salt: Option<&[u8; 32]>) {
61+
self.code_with_salt = Some((code.clone(), salt.is_some()));
62+
}
63+
5264
fn enter_child_span(
5365
&mut self,
5466
from: H160,
@@ -60,20 +72,36 @@ impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracing for CallTracer<Gas, Gas
6072
gas_left: Weight,
6173
) {
6274
if self.traces.is_empty() || !self.config.only_top_call {
63-
let call_type = if is_read_only {
64-
CallType::StaticCall
65-
} else if is_delegate_call {
66-
CallType::DelegateCall
67-
} else {
68-
CallType::Call
75+
let (call_type, input) = match self.code_with_salt.take() {
76+
Some((Code::Upload(v), salt)) => (
77+
if salt { CallType::Create2 } else { CallType::Create },
78+
v.into_iter().chain(input.to_vec().into_iter()).collect::<Vec<_>>(),
79+
),
80+
Some((Code::Existing(v), salt)) => (
81+
if salt { CallType::Create2 } else { CallType::Create },
82+
v.to_fixed_bytes()
83+
.into_iter()
84+
.chain(input.to_vec().into_iter())
85+
.collect::<Vec<_>>(),
86+
),
87+
None => {
88+
let call_type = if is_read_only {
89+
CallType::StaticCall
90+
} else if is_delegate_call {
91+
CallType::DelegateCall
92+
} else {
93+
CallType::Call
94+
};
95+
(call_type, input.to_vec())
96+
},
6997
};
7098

7199
self.traces.push(CallTrace {
72100
from,
73101
to,
74102
value: if is_read_only { None } else { Some(value) },
75103
call_type,
76-
input: input.to_vec().into(),
104+
input: input.into(),
77105
gas: (self.gas_mapper)(gas_left),
78106
..Default::default()
79107
});
@@ -102,6 +130,8 @@ impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracing for CallTracer<Gas, Gas
102130
}
103131

104132
fn exit_child_span(&mut self, output: &ExecReturnValue, gas_used: Weight) {
133+
self.code_with_salt = None;
134+
105135
// Set the output of the current trace
106136
let current_index = self.current_stack.pop().unwrap();
107137

@@ -126,6 +156,8 @@ impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracing for CallTracer<Gas, Gas
126156
}
127157
}
128158
fn exit_child_span_with_error(&mut self, error: DispatchError, gas_used: Weight) {
159+
self.code_with_salt = None;
160+
129161
// Set the output of the current trace
130162
let current_index = self.current_stack.pop().unwrap();
131163

substrate/frame/revive/src/exec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,7 @@ where
16901690
self.is_read_only(),
16911691
)?;
16921692
let address = T::AddressMapper::to_address(&self.top_frame().account_id);
1693+
if_tracing(|t| t.instantiate_code(&crate::Code::Existing(code_hash), salt));
16931694
self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data, BumpNonce::Yes)
16941695
.map(|_| address)
16951696
}

substrate/frame/revive/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use crate::{
5050
exec::{AccountIdOf, ExecError, Executable, Key, Stack as ExecStack},
5151
gas::GasMeter,
5252
storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
53+
tracing::if_tracing,
5354
vm::{CodeInfo, ContractBlob, RuntimeCosts},
5455
};
5556
use alloc::{boxed::Box, format, vec};
@@ -1113,6 +1114,8 @@ where
11131114

11141115
let try_instantiate = || {
11151116
let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1117+
1118+
if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
11161119
let (executable, upload_deposit) = match code {
11171120
Code::Upload(code) => {
11181121
let upload_account = T::UploadOrigin::ensure_origin(origin)?;

substrate/frame/revive/src/tests.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4220,6 +4220,66 @@ fn call_tracing_works() {
42204220
});
42214221
}
42224222

4223+
#[test]
4224+
fn create_call_tracing_works() {
4225+
use crate::evm::*;
4226+
let (code, code_hash) = compile_module("create2_with_value").unwrap();
4227+
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
4228+
let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000);
4229+
4230+
let mut tracer = CallTracer::new(Default::default(), |_| U256::zero());
4231+
4232+
let Contract { addr, .. } = trace(&mut tracer, || {
4233+
builder::bare_instantiate(Code::Upload(code.clone()))
4234+
.value(100)
4235+
.salt(None)
4236+
.build_and_unwrap_contract()
4237+
});
4238+
4239+
let call_trace = tracer.collect_trace().unwrap();
4240+
assert_eq!(
4241+
call_trace,
4242+
CallTrace {
4243+
from: ALICE_ADDR,
4244+
to: addr,
4245+
value: Some(100.into()),
4246+
input: Bytes(code.clone()),
4247+
call_type: CallType::Create,
4248+
..Default::default()
4249+
}
4250+
);
4251+
4252+
let mut tracer = CallTracer::new(Default::default(), |_| U256::zero());
4253+
let data = b"garbage";
4254+
let input = (code_hash, data).encode();
4255+
trace(&mut tracer, || {
4256+
assert_ok!(builder::call(addr).data(input.clone()).build());
4257+
});
4258+
4259+
let call_trace = tracer.collect_trace().unwrap();
4260+
let child_addr = crate::address::create2(&addr, &code, data, &[1u8; 32]);
4261+
4262+
assert_eq!(
4263+
call_trace,
4264+
CallTrace {
4265+
from: ALICE_ADDR,
4266+
to: addr,
4267+
value: Some(0.into()),
4268+
input: input.clone().into(),
4269+
calls: vec![CallTrace {
4270+
from: addr,
4271+
input: input.clone().into(),
4272+
to: child_addr,
4273+
value: Some(0.into()),
4274+
call_type: CallType::Create2,
4275+
..Default::default()
4276+
},],
4277+
..Default::default()
4278+
}
4279+
);
4280+
});
4281+
}
4282+
42234283
#[test]
42244284
fn prestate_tracing_works() {
42254285
use crate::evm::*;

substrate/frame/revive/src/tracing.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
1717

18-
use crate::{primitives::ExecReturnValue, DispatchError, Key, Weight};
18+
use crate::{primitives::ExecReturnValue, Code, DispatchError, Key, Weight};
1919
use alloc::vec::Vec;
2020
use environmental::environmental;
2121
use sp_core::{H160, H256, U256};
@@ -57,6 +57,10 @@ pub trait Tracing {
5757
_gas: Weight,
5858
) {
5959
}
60+
61+
/// Record the next code and salt to be instantiated.
62+
fn instantiate_code(&mut self, _code: &Code, _salt: Option<&[u8; 32]>) {}
63+
6064
/// Called when a balance is read
6165
fn balance_read(&mut self, _addr: &H160, _value: U256) {}
6266

0 commit comments

Comments
 (0)