diff --git a/src/tracing/js/bindings.rs b/src/tracing/js/bindings.rs index b71bcaed..f3f3c051 100644 --- a/src/tracing/js/bindings.rs +++ b/src/tracing/js/bindings.rs @@ -6,7 +6,7 @@ use crate::tracing::{ address_to_buf, bytes_to_address, bytes_to_hash, from_buf, to_bigint, to_buf, to_buf_value, }, - JsDbRequest, + JsDbRequest, TransactionContext, }, types::CallKind, }; @@ -624,9 +624,7 @@ pub(crate) struct EvmContext { pub(crate) output: Bytes, /// Number, block number pub(crate) time: String, - pub(crate) block_hash: Option, - pub(crate) tx_index: Option, - pub(crate) tx_hash: Option, + pub(crate) transaction_ctx: TransactionContext, } impl EvmContext { @@ -644,9 +642,7 @@ impl EvmContext { block, output, time, - block_hash, - tx_index, - tx_hash, + transaction_ctx, } = self; let obj = JsObject::default(); @@ -669,13 +665,13 @@ impl EvmContext { obj.set("block", block, false, ctx)?; obj.set("output", to_buf(output.to_vec(), ctx)?, false, ctx)?; obj.set("time", time, false, ctx)?; - if let Some(block_hash) = block_hash { + if let Some(block_hash) = transaction_ctx.block_hash { obj.set("blockHash", to_buf(block_hash.as_slice().to_vec(), ctx)?, false, ctx)?; } - if let Some(tx_index) = tx_index { + if let Some(tx_index) = transaction_ctx.tx_index { obj.set("txIndex", tx_index as u64, false, ctx)?; } - if let Some(tx_hash) = tx_hash { + if let Some(tx_hash) = transaction_ctx.tx_hash { obj.set("txHash", to_buf(tx_hash.as_slice().to_vec(), ctx)?, false, ctx)?; } diff --git a/src/tracing/js/mod.rs b/src/tracing/js/mod.rs index 05bf57a7..a2be9ad6 100644 --- a/src/tracing/js/mod.rs +++ b/src/tracing/js/mod.rs @@ -31,9 +31,13 @@ pub(crate) mod builtins; pub struct JsInspector { ctx: Context<'static>, /// The javascript config provided to the inspector. - _config: JsValue, + _js_config_value: JsValue, + /// The input config object. + config: serde_json::Value, /// The evaluated object that contains the inspector functions. obj: JsObject, + /// The context of the transaction that is being inspected. + transaction_context: TransactionContext, /// The javascript function that will be called when the result is requested. result_fn: JsObject, @@ -80,6 +84,19 @@ impl JsInspector { code: String, config: serde_json::Value, to_db_service: mpsc::Sender, + ) -> Result { + Self::with_transaction_context(code, config, to_db_service, Default::default()) + } + + /// Creates a new inspector from a javascript code snippet. See also [Self::new]. + /// + /// This also accepts a [TransactionContext] that gives the JS code access to some contextual + /// transaction infos. + pub fn with_transaction_context( + code: String, + config: serde_json::Value, + to_db_service: mpsc::Sender, + transaction_context: TransactionContext, ) -> Result { // Instantiate the execution context let mut ctx = Context::default(); @@ -116,7 +133,7 @@ impl JsInspector { let exit_fn = obj.get("exit", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); let step_fn = obj.get("step", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); - let config = + let _js_config_value = JsValue::from_json(&config, &mut ctx).map_err(JsInspectorError::InvalidJsonConfig)?; if let Some(setup_fn) = obj.get("setup", &mut ctx)?.as_object() { @@ -126,14 +143,16 @@ impl JsInspector { // call setup() setup_fn - .call(&(obj.clone().into()), &[config.clone()], &mut ctx) + .call(&(obj.clone().into()), &[_js_config_value.clone()], &mut ctx) .map_err(JsInspectorError::SetupCallFailed)?; } Ok(Self { ctx, - _config: config, + _js_config_value, + config, obj, + transaction_context, result_fn, fault_fn, enter_fn, @@ -145,6 +164,21 @@ impl JsInspector { }) } + /// Returns the config object. + pub fn config(&self) -> &serde_json::Value { + &self.config + } + + /// Returns the transaction context. + pub fn transaction_context(&self) -> &TransactionContext { + &self.transaction_context + } + + /// Sets the transaction context. + pub fn set_transaction_context(&mut self, transaction_context: TransactionContext) { + self.transaction_context = transaction_context; + } + /// Calls the result function and returns the result as [serde_json::Value]. /// /// Note: This is supposed to be called after the inspection has finished. @@ -199,11 +233,8 @@ impl JsInspector { block: env.block.number.try_into().unwrap_or(u64::MAX), output: output_bytes.unwrap_or_default(), time: env.block.timestamp.to_string(), - // TODO: fill in the following fields intrinsic_gas: 0, - block_hash: None, - tx_index: None, - tx_hash: None, + transaction_ctx: self.transaction_context.clone(), }; let ctx = ctx.into_js_object(&mut self.ctx)?; let db = db.into_js_object(&mut self.ctx)?; @@ -510,6 +541,44 @@ where } } +/// Contains some contextual infos for a transaction execution that is made available to the JS +/// object. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct TransactionContext { + /// Hash of the block the tx is contained within. + /// + /// `None` if this is a call. + pub block_hash: Option, + /// Index of the transaction within a block. + /// + /// `None` if this is a call. + pub tx_index: Option, + /// Hash of the transaction being traced. + /// + /// `None` if this is a call. + pub tx_hash: Option, +} + +impl TransactionContext { + /// Sets the block hash. + pub fn with_block_hash(mut self, block_hash: B256) -> Self { + self.block_hash = Some(block_hash); + self + } + + /// Sets the index of the transaction within a block. + pub fn with_tx_index(mut self, tx_index: usize) -> Self { + self.tx_index = Some(tx_index); + self + } + + /// Sets the hash of the transaction. + pub fn with_tx_hash(mut self, tx_hash: B256) -> Self { + self.tx_hash = Some(tx_hash); + self + } +} + /// Request variants to be sent from the inspector to the database #[derive(Debug, Clone)] pub enum JsDbRequest {