From bd9b993215db42e555cf400bc8b32a5c43be0e51 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 16 Jun 2023 18:04:17 +0200 Subject: [PATCH] test: more js testing support (#3211) --- .../assets/tracer-template.js | 24 ++++ crates/rpc/rpc-testing-util/src/debug.rs | 123 +++++++++++++++++- 2 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 crates/rpc/rpc-testing-util/assets/tracer-template.js diff --git a/crates/rpc/rpc-testing-util/assets/tracer-template.js b/crates/rpc/rpc-testing-util/assets/tracer-template.js new file mode 100644 index 000000000..29e033dc7 --- /dev/null +++ b/crates/rpc/rpc-testing-util/assets/tracer-template.js @@ -0,0 +1,24 @@ +{ + // called once + setup: function(cfg) { + // + }, + // required function that is invoked when a step fails + fault: function(log, db) { + // + }, + // required function that returns the result of the tracer: empty object + result: function(ctx, db) { + // + }, + // optional function that is invoked for every opcode + step: function(log, db) { + // + }, + enter: function(frame) { + // + }, + exit: function(res) { + // + } +} \ No newline at end of file diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs index da94fb3ee..2aad67e17 100644 --- a/crates/rpc/rpc-testing-util/src/debug.rs +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -5,6 +5,7 @@ use reth_rpc_api::clients::DebugApiClient; use reth_rpc_types::trace::geth::{GethDebugTracerType, GethDebugTracingOptions}; const NOOP_TRACER: &str = include_str!("../assets/noop-tracer.js"); +const JS_TRACER_TEMPLATE: &str = include_str!("../assets/tracer-template.js"); /// An extension trait for the Trace API. #[async_trait::async_trait] @@ -36,6 +37,106 @@ impl DebugApiExt for T { } } +/// A helper type that can be used to build a javascript tracer. +#[derive(Debug, Clone, Default)] +pub struct JsTracerBuilder { + setup_body: Option, + fault_body: Option, + result_body: Option, + enter_body: Option, + step_body: Option, + exit_body: Option, +} + +impl JsTracerBuilder { + /// Sets the body of the fault function + /// + /// The body code has access to the `log` and `db` variables. + pub fn fault_body(mut self, body: impl Into) -> Self { + self.fault_body = Some(body.into()); + self + } + + /// Sets the body of the setup function + /// + /// This body includes the `cfg` object variable + pub fn setup_body(mut self, body: impl Into) -> Self { + self.setup_body = Some(body.into()); + self + } + + /// Sets the body of the result function + /// + /// The body code has access to the `ctx` and `db` variables. + /// + /// ``` + /// use reth_rpc_api_testing_util::debug::JsTracerBuilder; + /// let code = JsTracerBuilder::default().result_body("return {};").code(); + /// ``` + pub fn result_body(mut self, body: impl Into) -> Self { + self.result_body = Some(body.into()); + self + } + + /// Sets the body of the enter function + /// + /// The body code has access to the `frame` variable. + pub fn enter_body(mut self, body: impl Into) -> Self { + self.enter_body = Some(body.into()); + self + } + + /// Sets the body of the step function + /// + /// The body code has access to the `log` and `db` variables. + pub fn step_body(mut self, body: impl Into) -> Self { + self.step_body = Some(body.into()); + self + } + + /// Sets the body of the exit function + /// + /// The body code has access to the `res` variable. + pub fn exit_body(mut self, body: impl Into) -> Self { + self.exit_body = Some(body.into()); + self + } + + /// Returns the tracers JS code + pub fn code(self) -> String { + let mut template = JS_TRACER_TEMPLATE.to_string(); + template = template.replace("//", self.setup_body.as_deref().unwrap_or_default()); + template = template.replace("//", self.fault_body.as_deref().unwrap_or_default()); + template = + template.replace("//", self.result_body.as_deref().unwrap_or("return {};")); + template = template.replace("//", self.step_body.as_deref().unwrap_or_default()); + template = template.replace("//", self.enter_body.as_deref().unwrap_or_default()); + template = template.replace("//", self.exit_body.as_deref().unwrap_or_default()); + template + } +} + +impl std::fmt::Display for JsTracerBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.clone().code()) + } +} + +impl From for GethDebugTracingOptions { + fn from(b: JsTracerBuilder) -> Self { + GethDebugTracingOptions { + tracer: Some(GethDebugTracerType::JsTracer(b.code())), + tracer_config: serde_json::Value::Object(Default::default()).into(), + ..Default::default() + } + } +} +impl From for Option { + fn from(b: JsTracerBuilder) -> Self { + Some(b.into()) + } +} + /// A javascript tracer that does nothing #[derive(Debug, Clone, Copy, Default)] #[non_exhaustive] @@ -59,21 +160,35 @@ impl From for Option { #[cfg(test)] mod tests { use crate::{ - debug::{DebugApiExt, NoopJsTracer}, + debug::{DebugApiExt, JsTracerBuilder, NoopJsTracer}, utils::parse_env_url, }; use jsonrpsee::http_client::HttpClientBuilder; + // random tx + const TX_1: &str = "0x5525c63a805df2b83c113ebcc8c7672a3b290673c4e81335b410cd9ebc64e085"; + #[tokio::test] #[ignore] async fn can_trace_noop_sepolia() { - // random tx - let tx = - "0x5525c63a805df2b83c113ebcc8c7672a3b290673c4e81335b410cd9ebc64e085".parse().unwrap(); + let tx = TX_1.parse().unwrap(); let url = parse_env_url("RETH_RPC_TEST_NODE_URL").unwrap(); let client = HttpClientBuilder::default().build(url).unwrap(); let res = client.debug_trace_transaction_json(tx, NoopJsTracer::default().into()).await.unwrap(); assert_eq!(res, serde_json::Value::Object(Default::default())); } + + #[tokio::test] + #[ignore] + async fn can_trace_default_template() { + let tx = TX_1.parse().unwrap(); + let url = parse_env_url("RETH_RPC_TEST_NODE_URL").unwrap(); + let client = HttpClientBuilder::default().build(url).unwrap(); + let res = client + .debug_trace_transaction_json(tx, JsTracerBuilder::default().into()) + .await + .unwrap(); + assert_eq!(res, serde_json::Value::Object(Default::default())); + } }