referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/api/
debug_rpc_types.rs

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
18use crate::evm::Bytes;
19use alloc::{collections::BTreeMap, string::String, vec::Vec};
20use codec::{Decode, Encode};
21use derive_more::From;
22use scale_info::TypeInfo;
23use serde::{
24	de::{Error, MapAccess, Visitor},
25	ser::{SerializeMap, Serializer},
26	Deserialize, Serialize,
27};
28use sp_core::{H160, H256, U256};
29
30/// The type of tracer to use.
31#[derive(TypeInfo, Debug, Clone, Encode, Decode, Serialize, Deserialize, PartialEq)]
32#[serde(tag = "tracer", content = "tracerConfig", rename_all = "camelCase")]
33pub enum TracerType {
34	/// A tracer that traces calls.
35	CallTracer(Option<CallTracerConfig>),
36
37	/// A tracer that traces the prestate.
38	PrestateTracer(Option<PrestateTracerConfig>),
39}
40
41impl From<CallTracerConfig> for TracerType {
42	fn from(config: CallTracerConfig) -> Self {
43		TracerType::CallTracer(Some(config))
44	}
45}
46
47impl Default for TracerType {
48	fn default() -> Self {
49		TracerType::CallTracer(Some(CallTracerConfig::default()))
50	}
51}
52
53/// Tracer configuration used to trace calls.
54#[derive(TypeInfo, Debug, Clone, Default, PartialEq)]
55#[cfg_attr(feature = "std", derive(Deserialize, Serialize), serde(rename_all = "camelCase"))]
56pub struct TracerConfig {
57	/// The tracer type.
58	#[cfg_attr(feature = "std", serde(flatten, default))]
59	pub config: TracerType,
60
61	/// Timeout for the tracer.
62	#[cfg_attr(feature = "std", serde(with = "humantime_serde", default))]
63	pub timeout: Option<core::time::Duration>,
64}
65
66/// The configuration for the call tracer.
67#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)]
68#[serde(default, rename_all = "camelCase")]
69pub struct CallTracerConfig {
70	/// Whether to include logs in the trace.
71	pub with_logs: bool,
72
73	/// Whether to only include the top-level calls in the trace.
74	pub only_top_call: bool,
75}
76
77impl Default for CallTracerConfig {
78	fn default() -> Self {
79		Self { with_logs: true, only_top_call: false }
80	}
81}
82
83/// The configuration for the prestate tracer.
84#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)]
85#[serde(default, rename_all = "camelCase")]
86pub struct PrestateTracerConfig {
87	/// Whether to include the diff mode in the trace.
88	pub diff_mode: bool,
89
90	/// Whether to include storage in the trace.
91	pub disable_storage: bool,
92
93	/// Whether to include code in the trace.
94	pub disable_code: bool,
95}
96
97impl Default for PrestateTracerConfig {
98	fn default() -> Self {
99		Self { diff_mode: false, disable_storage: false, disable_code: false }
100	}
101}
102
103/// Serialization should support the following JSON format:
104///
105/// ```json
106/// { "tracer": "callTracer", "tracerConfig": { "withLogs": false } }
107/// ```
108///
109/// ```json
110/// { "tracer": "callTracer" }
111/// ```
112#[test]
113fn test_tracer_config_serialization() {
114	let tracers = vec![
115		(
116			r#"{"tracer": "callTracer"}"#,
117			TracerConfig { config: TracerType::CallTracer(None), timeout: None },
118		),
119		(
120			r#"{"tracer": "callTracer", "tracerConfig": { "withLogs": false }}"#,
121			TracerConfig {
122				config: CallTracerConfig { with_logs: false, only_top_call: false }.into(),
123				timeout: None,
124			},
125		),
126		(
127			r#"{"tracer": "callTracer", "tracerConfig": { "onlyTopCall": true }}"#,
128			TracerConfig {
129				config: CallTracerConfig { with_logs: true, only_top_call: true }.into(),
130				timeout: None,
131			},
132		),
133		(
134			r#"{"tracer": "callTracer", "tracerConfig": { "onlyTopCall": true }, "timeout": "10ms"}"#,
135			TracerConfig {
136				config: CallTracerConfig { with_logs: true, only_top_call: true }.into(),
137				timeout: Some(core::time::Duration::from_millis(10)),
138			},
139		),
140	];
141
142	for (json_data, expected) in tracers {
143		let result: TracerConfig =
144			serde_json::from_str(json_data).expect("Deserialization should succeed");
145		assert_eq!(result, expected);
146	}
147}
148
149/// The type of call that was executed.
150#[derive(
151	Default, TypeInfo, Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Clone, Debug,
152)]
153#[serde(rename_all = "UPPERCASE")]
154pub enum CallType {
155	/// A regular call.
156	#[default]
157	Call,
158	/// A read-only call.
159	StaticCall,
160	/// A delegate call.
161	DelegateCall,
162	/// A create call.
163	Create,
164	/// A create2 call.
165	Create2,
166	/// A selfdestruct call.
167	Selfdestruct,
168}
169
170/// A Trace
171#[derive(TypeInfo, Deserialize, Serialize, From, Encode, Decode, Clone, Debug, Eq, PartialEq)]
172#[serde(untagged)]
173pub enum Trace {
174	/// A call trace.
175	Call(CallTrace),
176	/// A prestate trace.
177	Prestate(PrestateTrace),
178}
179
180/// A prestate Trace
181#[derive(TypeInfo, Encode, Serialize, Decode, Clone, Debug, Eq, PartialEq)]
182#[serde(untagged)]
183pub enum PrestateTrace {
184	/// The Prestate mode returns the accounts necessary to execute a given transaction
185	Prestate(BTreeMap<H160, PrestateTraceInfo>),
186
187	/// The diff mode returns the differences between the transaction's pre and post-state
188	/// The result only contains the accounts that were modified by the transaction
189	DiffMode {
190		/// The state before the call.
191		///  The accounts in the `pre` field will contain all of their basic fields, even if those
192		/// fields have not been modified. For `storage` however, only non-empty slots that have
193		/// been modified will be included
194		pre: BTreeMap<H160, PrestateTraceInfo>,
195		/// The state after the call.
196		/// It only contains the specific fields that were actually modified during the transaction
197		post: BTreeMap<H160, PrestateTraceInfo>,
198	},
199}
200
201impl<'de> Deserialize<'de> for PrestateTrace {
202	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203	where
204		D: serde::Deserializer<'de>,
205	{
206		struct PrestateTraceVisitor;
207
208		impl<'de> Visitor<'de> for PrestateTraceVisitor {
209			type Value = PrestateTrace;
210
211			fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
212				formatter.write_str("a map representing either Prestate or DiffMode")
213			}
214
215			fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
216			where
217				A: MapAccess<'de>,
218			{
219				let mut pre_map = None;
220				let mut post_map = None;
221				let mut account_map = BTreeMap::new();
222
223				while let Some(key) = map.next_key::<String>()? {
224					match key.as_str() {
225						"pre" => {
226							if pre_map.is_some() {
227								return Err(Error::duplicate_field("pre"));
228							}
229							pre_map = Some(map.next_value::<BTreeMap<H160, PrestateTraceInfo>>()?);
230						},
231						"post" => {
232							if post_map.is_some() {
233								return Err(Error::duplicate_field("post"));
234							}
235							post_map = Some(map.next_value::<BTreeMap<H160, PrestateTraceInfo>>()?);
236						},
237						_ => {
238							let addr: H160 =
239								key.parse().map_err(|_| Error::custom("Invalid address"))?;
240							let info = map.next_value::<PrestateTraceInfo>()?;
241							account_map.insert(addr, info);
242						},
243					}
244				}
245
246				match (pre_map, post_map) {
247					(Some(pre), Some(post)) => {
248						if !account_map.is_empty() {
249							return Err(Error::custom("Mixed diff and prestate mode"));
250						}
251						Ok(PrestateTrace::DiffMode { pre, post })
252					},
253					(None, None) => Ok(PrestateTrace::Prestate(account_map)),
254					_ => Err(Error::custom("diff mode: must have both 'pre' and 'post'")),
255				}
256			}
257		}
258
259		deserializer.deserialize_map(PrestateTraceVisitor)
260	}
261}
262
263impl PrestateTrace {
264	/// Returns the pre and post trace info.
265	pub fn state_mut(
266		&mut self,
267	) -> (&mut BTreeMap<H160, PrestateTraceInfo>, Option<&mut BTreeMap<H160, PrestateTraceInfo>>) {
268		match self {
269			PrestateTrace::Prestate(pre) => (pre, None),
270			PrestateTrace::DiffMode { pre, post } => (pre, Some(post)),
271		}
272	}
273}
274
275/// The info of a prestate trace.
276#[derive(
277	TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
278)]
279pub struct PrestateTraceInfo {
280	/// The balance of the account.
281	#[serde(skip_serializing_if = "Option::is_none")]
282	pub balance: Option<U256>,
283	/// The nonce of the account.
284	#[serde(skip_serializing_if = "Option::is_none")]
285	pub nonce: Option<u32>,
286	/// The code of the contract account.
287	#[serde(skip_serializing_if = "Option::is_none")]
288	pub code: Option<Bytes>,
289	/// The storage of the contract account.
290	#[serde(default, skip_serializing_if = "is_empty", serialize_with = "serialize_map_skip_none")]
291	pub storage: BTreeMap<Bytes, Option<Bytes>>,
292}
293
294/// Returns true if the map has no `Some` element
295pub fn is_empty<K, V>(map: &BTreeMap<K, Option<V>>) -> bool {
296	!map.values().any(|v| v.is_some())
297}
298
299/// Serializes a map, skipping `None` values.
300pub fn serialize_map_skip_none<S, K, V>(
301	map: &BTreeMap<K, Option<V>>,
302	serializer: S,
303) -> Result<S::Ok, S::Error>
304where
305	S: Serializer,
306	K: serde::Serialize,
307	V: serde::Serialize,
308{
309	let len = map.values().filter(|v| v.is_some()).count();
310	let mut ser_map = serializer.serialize_map(Some(len))?;
311
312	for (key, opt_val) in map {
313		if let Some(val) = opt_val {
314			ser_map.serialize_entry(key, val)?;
315		}
316	}
317
318	ser_map.end()
319}
320
321/// A smart contract execution call trace.
322#[derive(
323	TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
324)]
325#[serde(rename_all = "camelCase")]
326pub struct CallTrace<Gas = U256> {
327	/// Address of the sender.
328	pub from: H160,
329	/// Amount of gas provided for the call.
330	pub gas: Gas,
331	/// Amount of gas used.
332	pub gas_used: Gas,
333	/// Address of the receiver.
334	pub to: H160,
335	/// Call input data.
336	pub input: Bytes,
337	/// Return data.
338	#[serde(skip_serializing_if = "Bytes::is_empty")]
339	pub output: Bytes,
340	/// The error message if the call failed.
341	#[serde(skip_serializing_if = "Option::is_none")]
342	pub error: Option<String>,
343	/// The revert reason, if the call reverted.
344	#[serde(skip_serializing_if = "Option::is_none")]
345	pub revert_reason: Option<String>,
346	/// List of sub-calls.
347	#[serde(skip_serializing_if = "Vec::is_empty")]
348	pub calls: Vec<CallTrace<Gas>>,
349	/// List of logs emitted during the call.
350	#[serde(skip_serializing_if = "Vec::is_empty")]
351	pub logs: Vec<CallLog>,
352	/// Amount of value transferred.
353	#[serde(skip_serializing_if = "Option::is_none")]
354	pub value: Option<U256>,
355	/// Type of call.
356	#[serde(rename = "type")]
357	pub call_type: CallType,
358	/// Number of child calls entered (for log position calculation)
359	#[serde(skip)]
360	pub child_call_count: u32,
361}
362
363/// A log emitted during a call.
364#[derive(
365	Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq,
366)]
367pub struct CallLog {
368	/// The address of the contract that emitted the log.
369	pub address: H160,
370	/// The topics used to index the log.
371	#[serde(default, skip_serializing_if = "Vec::is_empty")]
372	pub topics: Vec<H256>,
373	/// The log's data.
374	pub data: Bytes,
375	/// Position of the log relative to subcalls within the same trace
376	/// See <https://github.com/ethereum/go-ethereum/pull/28389> for details
377	#[serde(with = "super::hex_serde")]
378	pub position: u32,
379}
380
381/// A transaction trace
382#[derive(Serialize, Deserialize, Clone, Debug)]
383#[serde(rename_all = "camelCase")]
384pub struct TransactionTrace {
385	/// The transaction hash.
386	pub tx_hash: H256,
387	/// The trace of the transaction.
388	#[serde(rename = "result")]
389	pub trace: Trace,
390}