Skip to content

Commit

Permalink
Add position to tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Jan 3, 2025
1 parent c18ba8d commit f7f496b
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 178 deletions.
4 changes: 4 additions & 0 deletions substrate/frame/revive/fixtures/contracts/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub extern "C" fn call() {

let next_input = (calls_left - 1).to_le_bytes();

api::deposit_event(&[], b"before");

// Call the callee
api::call(
uapi::CallFlags::empty(),
Expand All @@ -47,6 +49,8 @@ pub extern "C" fn call() {
)
.unwrap();

api::deposit_event(&[], b"after");

if calls_left == 0 {
return
}
Expand Down
19 changes: 19 additions & 0 deletions substrate/frame/revive/rpc/examples/js/abi/TracingCaller.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "message",
"type": "string"
}
],
"name": "TraceEvent",
"type": "event"
},
{
"inputs": [],
"name": "callee",
Expand Down
19 changes: 19 additions & 0 deletions substrate/frame/revive/rpc/examples/js/abi/TracingCaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ export const TracingCallerAbi = [
stateMutability: "nonpayable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
{
indexed: false,
internalType: "string",
name: "message",
type: "string",
},
],
name: "TraceEvent",
type: "event",
},
{
inputs: [],
name: "callee",
Expand Down
9 changes: 6 additions & 3 deletions substrate/frame/revive/rpc/examples/js/contracts/Tracing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.0;

contract TracingCaller {
event TraceEvent(uint256 value, string message);
address public callee;

constructor(address _callee) {
Expand All @@ -11,13 +12,15 @@ contract TracingCaller {

function start(uint256 counter) external {
if (counter == 0) {
uint256 a = 1;
uint256 b = 0;
uint256 c = a / b;
// uint256 a = 1;
// uint256 b = 0;
// uint256 c = a / b;
return;
}

emit TraceEvent(counter, "before");
TracingCallee(callee).consumeGas();
emit TraceEvent(counter, "after");

try TracingCallee(callee).failingFunction() {
} catch {
Expand Down
Binary file not shown.
Binary file not shown.
49 changes: 25 additions & 24 deletions substrate/frame/revive/rpc/examples/js/src/geth-diff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,32 +371,33 @@ for (const env of envs) {
})()

console.error('Caller address:', callerAddress)
//const txHash = await (async () => {
// const { request } = await env.serverWallet.simulateContract({
// address: callerAddress,
// abi: TracingCallerAbi,
// functionName: 'start',
// args: [2n],
// })
//
// const hash = await env.serverWallet.writeContract(request)
// await env.serverWallet.waitForTransactionReceipt({ hash })
//
//
// return hash
//})()
const txHash = await (async () => {
const { request } = await env.serverWallet.simulateContract({
address: callerAddress,
abi: TracingCallerAbi,
functionName: 'start',
args: [2n],
})

let data = encodeFunctionData({
abi: TracingCallerAbi,
functionName: 'start',
args: [2n],
})
const hash = await env.serverWallet.writeContract(request)
await env.serverWallet.waitForTransactionReceipt({ hash })

const res = await env.debugClient.traceCall({
account: env.accountWallet.account,
data,
to: callerAddress,
})
return hash
})()
console.error('Tx hash:', txHash)
const res = await env.debugClient.traceTransaction(txHash)

//let data = encodeFunctionData({
// abi: TracingCallerAbi,
// functionName: 'start',
// args: [2n],
//})
//
//const res = await env.debugClient.traceCall({
// account: env.accountWallet.account,
// data,
// to: callerAddress,
//})

console.error(res)
Bun.write('/tmp/tracing.json', JSON.stringify(res, null, 2))
Expand Down
49 changes: 45 additions & 4 deletions substrate/frame/revive/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
// limitations under the License.

pub use crate::{
evm::{CallTrace, CallType, Traces},
evm::{CallLog, CallTrace, CallType, Traces},
exec::{ExecResult, ExportedFunction},
primitives::ExecReturnValue,
BalanceOf,
};
use crate::{Config, DispatchError, GasMeter, LOG_TARGET};
use alloc::vec::Vec;
use sp_core::{H160, U256};
use sp_core::{H160, H256, U256};

/// Umbrella trait for all interfaces that serves for debugging.
pub trait Debugger<T: Config>: CallInterceptor<T> {}
Expand Down Expand Up @@ -51,6 +51,9 @@ pub trait Tracing<T: Config>: Default {
gas_meter: &GasMeter<T>,
);

/// Record a log event
fn log_event(&mut self, event: &H160, topics: &[H256], data: &[u8]);

/// Called after a contract call is executed
fn exit_child_span(&mut self, output: &ExecReturnValue, gas_meter: &GasMeter<T>);

Expand All @@ -60,8 +63,8 @@ pub trait Tracing<T: Config>: Default {

impl Tracer {
/// Creates a new [`Tracer::CallTracer`].
pub fn new_call_tracer() -> Self {
Tracer::CallTracer(CallTracer::default())
pub fn new_call_tracer(with_log: bool) -> Self {
Tracer::CallTracer(CallTracer::new(with_log))
}

/// Returns the call tracer if it is enabled.
Expand Down Expand Up @@ -114,6 +117,17 @@ where
}
}

fn log_event(&mut self, event: &H160, topics: &[H256], data: &[u8]) {
match self {
Tracer::CallTracer(tracer) => {
<CallTracer as Tracing<T>>::log_event(tracer, event, topics, data);
},
Tracer::Disabled => {
log::trace!(target: LOG_TARGET, "event {event:?} topics: {topics:?} data: {data:?}");
},
}
}

fn exit_child_span(&mut self, output: &ExecReturnValue, gas_meter: &GasMeter<T>) {
match self {
Tracer::CallTracer(tracer) => {
Expand Down Expand Up @@ -143,6 +157,14 @@ pub struct CallTracer {
pub traces: Vec<CallTrace>,
/// Stack of indices to the current active traces
current_stack: Vec<usize>,
/// whether or not to capture logs
with_log: bool,
}

impl CallTracer {
pub fn new(with_log: bool) -> Self {
Self { traces: Vec::new(), current_stack: Vec::new(), with_log }
}
}

impl<T: Config> Tracing<T> for CallTracer
Expand Down Expand Up @@ -180,6 +202,25 @@ where
// Push the index onto the stack of the current active trace
self.current_stack.push(self.traces.len() - 1);
}

fn log_event(&mut self, address: &H160, topics: &[H256], data: &[u8]) {
if !self.with_log {
return;
}

let current_index = self.current_stack.last().unwrap();
let position = self.traces[*current_index].calls.len() as u32;
let log = CallLog {
address: *address,
topics: topics.to_vec(),
data: data.to_vec().into(),
position,
};

let current_index = *self.current_stack.last().unwrap();
self.traces[current_index].logs.push(log);
}

fn exit_child_span(&mut self, output: &ExecReturnValue, gas_meter: &GasMeter<T>) {
// Set the output of the current trace
let current_index = self.current_stack.pop().unwrap();
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/revive/src/evm/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// limitations under the License.
//! JSON-RPC methods and types, for Ethereum.
mod hex_serde;

mod byte;
pub use byte::*;

Expand Down
74 changes: 9 additions & 65 deletions substrate/frame/revive/src/evm/api/byte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Define Byte wrapper types for encoding and decoding hex strings
use super::hex_serde::HexCodec;
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode};
use core::{
fmt::{Debug, Display, Formatter, Result as FmtResult},
str::FromStr,
};
use hex_serde::HexCodec;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};

mod hex_serde {
#[cfg(not(feature = "std"))]
use alloc::{format, string::String, vec::Vec};
use serde::{Deserialize, Deserializer, Serializer};

pub trait HexCodec: Sized {
type Error;
fn to_hex(&self) -> String;
fn from_hex(s: String) -> Result<Self, Self::Error>;
}

impl HexCodec for u8 {
type Error = core::num::ParseIntError;
fn to_hex(&self) -> String {
format!("0x{:x}", self)
}
fn from_hex(s: String) -> Result<Self, Self::Error> {
u8::from_str_radix(s.trim_start_matches("0x"), 16)
}
}

impl<const T: usize> HexCodec for [u8; T] {
type Error = hex::FromHexError;
fn to_hex(&self) -> String {
format!("0x{}", hex::encode(self))
}
fn from_hex(s: String) -> Result<Self, Self::Error> {
let data = hex::decode(s.trim_start_matches("0x"))?;
data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength)
}
}

impl HexCodec for Vec<u8> {
type Error = hex::FromHexError;
fn to_hex(&self) -> String {
format!("0x{}", hex::encode(self))
}
fn from_hex(s: String) -> Result<Self, Self::Error> {
hex::decode(s.trim_start_matches("0x"))
}
}

pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: HexCodec,
{
let s = value.to_hex();
serializer.serialize_str(&s)
}

pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: HexCodec,
<T as HexCodec>::Error: core::fmt::Debug,
{
let s = String::deserialize(deserializer)?;
let value = T::from_hex(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?;
Ok(value)
}
}

impl FromStr for Bytes {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand All @@ -100,7 +37,7 @@ macro_rules! impl_hex {
($type:ident, $inner:ty, $default:expr) => {
#[derive(Encode, Decode, Eq, PartialEq, TypeInfo, Clone, Serialize, Deserialize)]
#[doc = concat!("`", stringify!($inner), "`", " wrapper type for encoding and decoding hex strings")]
pub struct $type(#[serde(with = "hex_serde")] pub $inner);
pub struct $type(#[serde(with = "crate::evm::api::hex_serde")] pub $inner);

impl Default for $type {
fn default() -> Self {
Expand Down Expand Up @@ -131,6 +68,13 @@ macro_rules! impl_hex {
};
}

impl Bytes {
/// See `Vec::is_empty`
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

impl_hex!(Byte, u8, 0u8);
impl_hex!(Bytes, Vec<u8>, vec![]);
impl_hex!(Bytes8, [u8; 8], [0u8; 8]);
Expand Down
Loading

0 comments on commit f7f496b

Please sign in to comment.