diff --git a/core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json b/core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json similarity index 52% rename from core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json rename to core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json index f46674b08bc6..f8e9e650d10a 100644 --- a/core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json +++ b/core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n transactions.hash AS tx_hash,\n transactions.index_in_block AS tx_index_in_block,\n call_trace\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n transactions.miniblock_number = $1\n ORDER BY\n transactions.index_in_block\n ", + "query": "\n SELECT\n transactions.hash AS tx_hash,\n transactions.index_in_block AS tx_index_in_block,\n call_trace,\n transactions.error AS tx_error\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n transactions.miniblock_number = $1\n ORDER BY\n transactions.index_in_block\n ", "describe": { "columns": [ { @@ -17,6 +17,11 @@ "ordinal": 2, "name": "call_trace", "type_info": "Bytea" + }, + { + "ordinal": 3, + "name": "tx_error", + "type_info": "Varchar" } ], "parameters": { @@ -27,8 +32,9 @@ "nullable": [ false, true, - false + false, + true ] }, - "hash": "7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561" + "hash": "34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe" } diff --git a/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json b/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json deleted file mode 100644 index dbeaede9ecd2..000000000000 --- a/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n call_trace\n FROM\n call_traces\n WHERE\n tx_hash = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "call_trace", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Bytea" - ] - }, - "nullable": [ - false - ] - }, - "hash": "87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25" -} diff --git a/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json b/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json new file mode 100644 index 000000000000..abdc0a1cfa87 --- /dev/null +++ b/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n call_trace,\n transactions.error AS tx_error\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n tx_hash = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "call_trace", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "tx_error", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Bytea" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947" +} diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 229f49da6e37..96513da54264 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -567,7 +567,8 @@ impl BlocksWeb3Dal<'_, '_> { SELECT transactions.hash AS tx_hash, transactions.index_in_block AS tx_index_in_block, - call_trace + call_trace, + transactions.error AS tx_error FROM call_traces INNER JOIN transactions ON tx_hash = transactions.hash @@ -583,7 +584,7 @@ impl BlocksWeb3Dal<'_, '_> { .fetch_all(self.storage) .await? .into_iter() - .map(|call_trace| { + .map(|mut call_trace| { let tx_hash = H256::from_slice(&call_trace.tx_hash); let index = call_trace.tx_index_in_block.unwrap_or_default() as usize; let meta = CallTraceMeta { @@ -591,6 +592,7 @@ impl BlocksWeb3Dal<'_, '_> { tx_hash, block_number: block_number.0, block_hash, + internal_error: call_trace.tx_error.take(), }; (call_trace.into_call(protocol_version), meta) }) diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index cceebc85cf2b..27442e41d7be 100644 --- a/core/lib/dal/src/models/storage_transaction.rs +++ b/core/lib/dal/src/models/storage_transaction.rs @@ -591,6 +591,7 @@ pub(crate) struct CallTrace { pub call_trace: Vec, pub tx_hash: Vec, pub tx_index_in_block: Option, + pub tx_error: Option, } impl CallTrace { diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index b4e31954be76..6b35c507b919 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -2233,9 +2233,11 @@ impl TransactionsDal<'_, '_> { Ok(sqlx::query!( r#" SELECT - call_trace + call_trace, + transactions.error AS tx_error FROM call_traces + INNER JOIN transactions ON tx_hash = transactions.hash WHERE tx_hash = $1 "#, @@ -2245,7 +2247,7 @@ impl TransactionsDal<'_, '_> { .with_arg("tx_hash", &tx_hash) .fetch_optional(self.storage) .await? - .map(|call_trace| { + .map(|mut call_trace| { ( parse_call_trace(&call_trace.call_trace, protocol_version), CallTraceMeta { @@ -2253,6 +2255,7 @@ impl TransactionsDal<'_, '_> { tx_hash, block_number: row.miniblock_number as u32, block_hash: H256::from_slice(&row.miniblocks_hash), + internal_error: call_trace.tx_error.take(), }, ) })) diff --git a/core/lib/types/src/debug_flat_call.rs b/core/lib/types/src/debug_flat_call.rs index 3488b0e5b42c..2f130019446a 100644 --- a/core/lib/types/src/debug_flat_call.rs +++ b/core/lib/types/src/debug_flat_call.rs @@ -49,4 +49,11 @@ pub struct CallTraceMeta { pub tx_hash: H256, pub block_number: u32, pub block_hash: H256, + /// Error message associated with the transaction in the sequencer database. + /// Can be used to identify a failed transaction if error information is not + /// recorded otherwise (e.g. out-of-gas errors in early protocol versions). + /// + /// Should be seen as a fallback value (e.g. if the trace doesn't contain the error + /// or revert reason). + pub internal_error: Option, } diff --git a/core/node/api_server/src/web3/namespaces/debug.rs b/core/node/api_server/src/web3/namespaces/debug.rs index 8e72f5b45991..98af15032008 100644 --- a/core/node/api_server/src/web3/namespaces/debug.rs +++ b/core/node/api_server/src/web3/namespaces/debug.rs @@ -31,13 +31,14 @@ impl DebugNamespace { pub(crate) fn map_call( call: Call, - meta: CallTraceMeta, + mut meta: CallTraceMeta, tracer_option: TracerConfig, ) -> CallTracerResult { match tracer_option.tracer { SupportedTracers::CallTracer => CallTracerResult::CallTrace(Self::map_default_call( call, tracer_option.tracer_config.only_top_call, + meta.internal_error, )), SupportedTracers::FlatCallTracer => { let mut calls = vec![]; @@ -47,19 +48,24 @@ impl DebugNamespace { &mut calls, &mut traces, tracer_option.tracer_config.only_top_call, - &meta, + &mut meta, ); CallTracerResult::FlatCallTrace(calls) } } } - pub(crate) fn map_default_call(call: Call, only_top_call: bool) -> DebugCall { + pub(crate) fn map_default_call( + call: Call, + only_top_call: bool, + internal_error: Option, + ) -> DebugCall { let calls = if only_top_call { vec![] } else { + // We don't need to propagate the internal error to the nested calls. call.calls .into_iter() - .map(|call| Self::map_default_call(call, false)) + .map(|call| Self::map_default_call(call, false, None)) .collect() }; let debug_type = match call.r#type { @@ -76,7 +82,7 @@ impl DebugNamespace { value: call.value, output: web3::Bytes::from(call.output), input: web3::Bytes::from(call.input), - error: call.error, + error: call.error.or(internal_error), revert_reason: call.revert_reason, calls, } @@ -87,7 +93,7 @@ impl DebugNamespace { calls: &mut Vec, trace_address: &mut Vec, only_top_call: bool, - meta: &CallTraceMeta, + meta: &mut CallTraceMeta, ) { let subtraces = call.calls.len(); let debug_type = match call.r#type { @@ -96,16 +102,24 @@ impl DebugNamespace { CallType::NearCall => unreachable!("We have to filter our near calls before"), }; - let (result, error) = match (call.revert_reason, call.error) { - (Some(revert_reason), _) => { + // We only want to set the internal error for topmost call, so we take it. + let internal_error = meta.internal_error.take(); + + let (result, error) = match (call.revert_reason, call.error, internal_error) { + (Some(revert_reason), _, _) => { // If revert_reason exists, it takes priority over VM error (None, Some(revert_reason)) } - (None, Some(vm_error)) => { + (None, Some(vm_error), _) => { // If no revert_reason but VM error exists (None, Some(vm_error)) } - (None, None) => ( + (None, None, Some(internal_error)) => { + // No VM error, but there is an error in the sequencer DB. + // Only to be set as a topmost error. + (None, Some(internal_error)) + } + (None, None, None) => ( Some(CallResult { output: web3::Bytes::from(call.output), gas_used: U256::from(call.gas_used), @@ -175,15 +189,19 @@ impl DebugNamespace { SupportedTracers::CallTracer => CallTracerBlockResult::CallTrace( call_traces .into_iter() - .map(|(call, _)| ResultDebugCall { - result: Self::map_default_call(call, options.tracer_config.only_top_call), + .map(|(call, meta)| ResultDebugCall { + result: Self::map_default_call( + call, + options.tracer_config.only_top_call, + meta.internal_error, + ), }) .collect(), ), SupportedTracers::FlatCallTracer => { let res = call_traces .into_iter() - .map(|(call, meta)| { + .map(|(call, mut meta)| { let mut traces = vec![meta.index_in_block]; let mut flat_calls = vec![]; Self::flatten_call( @@ -191,7 +209,7 @@ impl DebugNamespace { &mut flat_calls, &mut traces, options.tracer_config.only_top_call, - &meta, + &mut meta, ); ResultDebugCallFlat { tx_hash: meta.tx_hash, diff --git a/core/node/api_server/src/web3/tests/debug.rs b/core/node/api_server/src/web3/tests/debug.rs index 28a22511fa98..b2aae53eaa32 100644 --- a/core/node/api_server/src/web3/tests/debug.rs +++ b/core/node/api_server/src/web3/tests/debug.rs @@ -73,7 +73,7 @@ impl HttpTest for TraceBlockTest { let expected_calls: Vec<_> = tx_result .call_traces .iter() - .map(|call| DebugNamespace::map_default_call(call.clone(), false)) + .map(|call| DebugNamespace::map_default_call(call.clone(), false, None)) .collect(); assert_eq!(result.calls, expected_calls); } @@ -216,7 +216,7 @@ impl HttpTest for TraceTransactionTest { let expected_calls: Vec<_> = tx_results[0] .call_traces .iter() - .map(|call| DebugNamespace::map_default_call(call.clone(), false)) + .map(|call| DebugNamespace::map_default_call(call.clone(), false, None)) .collect(); let result = client