1use alloy_consensus::BlockHeader as _;
2use alloy_eips::BlockId;
3use alloy_evm::block::calc::{base_block_reward_pre_merge, block_reward, ommer_reward};
4use alloy_primitives::{
5 map::{HashMap, HashSet},
6 Address, BlockHash, Bytes, B256, U256,
7};
8use alloy_rpc_types_eth::{
9 state::{EvmOverrides, StateOverride},
10 BlockOverrides, Index,
11};
12use alloy_rpc_types_trace::{
13 filter::TraceFilter,
14 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
15 parity::*,
16 tracerequest::TraceCallRequest,
17};
18use async_trait::async_trait;
19use jsonrpsee::core::RpcResult;
20use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
21use reth_evm::ConfigureEvm;
22use reth_primitives_traits::{BlockBody, BlockHeader};
23use reth_rpc_api::TraceApiServer;
24use reth_rpc_convert::RpcTxReq;
25use reth_rpc_eth_api::{
26 helpers::{Call, LoadPendingBlock, LoadTransaction, Trace, TraceExt},
27 FromEthApiError, RpcNodeCore,
28};
29use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
30use reth_storage_api::{BlockNumReader, BlockReader};
31use reth_tasks::pool::BlockingTaskGuard;
32use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool};
33use revm::DatabaseCommit;
34use revm_inspectors::{
35 opcode::OpcodeGasInspector,
36 storage::StorageInspector,
37 tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig},
38};
39use serde::{Deserialize, Serialize};
40use std::sync::Arc;
41use tokio::sync::{AcquireError, OwnedSemaphorePermit};
42
43pub struct TraceApi<Eth> {
47 inner: Arc<TraceApiInner<Eth>>,
48}
49
50impl<Eth> TraceApi<Eth> {
53 pub fn new(
55 eth_api: Eth,
56 blocking_task_guard: BlockingTaskGuard,
57 eth_config: EthConfig,
58 ) -> Self {
59 let inner = Arc::new(TraceApiInner { eth_api, blocking_task_guard, eth_config });
60 Self { inner }
61 }
62
63 async fn acquire_trace_permit(
65 &self,
66 ) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
67 self.inner.blocking_task_guard.clone().acquire_owned().await
68 }
69
70 pub fn eth_api(&self) -> &Eth {
72 &self.inner.eth_api
73 }
74}
75
76impl<Eth: RpcNodeCore> TraceApi<Eth> {
77 pub fn provider(&self) -> &Eth::Provider {
79 self.inner.eth_api.provider()
80 }
81}
82
83impl<Eth> TraceApi<Eth>
86where
87 Eth: Trace + Call + LoadPendingBlock + LoadTransaction + 'static,
90{
91 pub async fn trace_call(
93 &self,
94 trace_request: TraceCallRequest<RpcTxReq<Eth::NetworkTypes>>,
95 ) -> Result<TraceResults, Eth::Error> {
96 let at = trace_request.block_id.unwrap_or_default();
97 let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
98 let overrides =
99 EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
100 let mut inspector = TracingInspector::new(config);
101 let this = self.clone();
102 self.eth_api()
103 .spawn_with_call_at(trace_request.call, at, overrides, move |db, evm_env, tx_env| {
104 let res = this.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
105 let trace_res = inspector
106 .into_parity_builder()
107 .into_trace_results_with_state(&res, &trace_request.trace_types, &db)
108 .map_err(Eth::Error::from_eth_err)?;
109 Ok(trace_res)
110 })
111 .await
112 }
113
114 pub async fn trace_raw_transaction(
116 &self,
117 tx: Bytes,
118 trace_types: HashSet<TraceType>,
119 block_id: Option<BlockId>,
120 ) -> Result<TraceResults, Eth::Error> {
121 let tx = recover_raw_transaction::<PoolPooledTx<Eth::Pool>>(&tx)?
122 .map(<Eth::Pool as TransactionPool>::Transaction::pooled_into_consensus);
123
124 let (evm_env, at) = self.eth_api().evm_env_at(block_id.unwrap_or_default()).await?;
125 let tx_env = self.eth_api().evm_config().tx_env(tx);
126
127 let config = TracingInspectorConfig::from_parity_config(&trace_types);
128
129 self.eth_api()
130 .spawn_trace_at_with_state(evm_env, tx_env, config, at, move |inspector, res, db| {
131 inspector
132 .into_parity_builder()
133 .into_trace_results_with_state(&res, &trace_types, &db)
134 .map_err(Eth::Error::from_eth_err)
135 })
136 .await
137 }
138
139 pub async fn trace_call_many(
144 &self,
145 calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
146 block_id: Option<BlockId>,
147 ) -> Result<Vec<TraceResults>, Eth::Error> {
148 let at = block_id.unwrap_or(BlockId::pending());
149 let (evm_env, at) = self.eth_api().evm_env_at(at).await?;
150
151 self.eth_api()
153 .spawn_with_state_at_block(at, move |eth_api, mut db| {
154 let mut results = Vec::with_capacity(calls.len());
155 let mut calls = calls.into_iter().peekable();
156
157 while let Some((call, trace_types)) = calls.next() {
158 let (evm_env, tx_env) = eth_api.prepare_call_env(
159 evm_env.clone(),
160 call,
161 &mut db,
162 Default::default(),
163 )?;
164 let config = TracingInspectorConfig::from_parity_config(&trace_types);
165 let mut inspector = TracingInspector::new(config);
166 let res = eth_api.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
167
168 let trace_res = inspector
169 .into_parity_builder()
170 .into_trace_results_with_state(&res, &trace_types, &db)
171 .map_err(Eth::Error::from_eth_err)?;
172
173 results.push(trace_res);
174
175 if calls.peek().is_some() {
178 db.commit(res.state)
179 }
180 }
181
182 Ok(results)
183 })
184 .await
185 }
186
187 pub async fn replay_transaction(
189 &self,
190 hash: B256,
191 trace_types: HashSet<TraceType>,
192 ) -> Result<TraceResults, Eth::Error> {
193 let config = TracingInspectorConfig::from_parity_config(&trace_types);
194 self.eth_api()
195 .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
196 let trace_res = inspector
197 .into_parity_builder()
198 .into_trace_results_with_state(&res, &trace_types, &db)
199 .map_err(Eth::Error::from_eth_err)?;
200 Ok(trace_res)
201 })
202 .await
203 .transpose()
204 .ok_or(EthApiError::TransactionNotFound)?
205 }
206
207 pub async fn trace_get(
214 &self,
215 hash: B256,
216 indices: Vec<usize>,
217 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
218 if indices.len() != 1 {
219 return Ok(None)
221 }
222 self.trace_get_index(hash, indices[0]).await
223 }
224
225 pub async fn trace_get_index(
229 &self,
230 hash: B256,
231 index: usize,
232 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
233 Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
234 }
235
236 pub async fn trace_transaction(
238 &self,
239 hash: B256,
240 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
241 self.eth_api()
242 .spawn_trace_transaction_in_block(
243 hash,
244 TracingInspectorConfig::default_parity(),
245 move |tx_info, inspector, _, _| {
246 let traces =
247 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
248 Ok(traces)
249 },
250 )
251 .await
252 }
253
254 pub async fn trace_transaction_opcode_gas(
257 &self,
258 tx_hash: B256,
259 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
260 self.eth_api()
261 .spawn_trace_transaction_in_block_with_inspector(
262 tx_hash,
263 OpcodeGasInspector::default(),
264 move |_tx_info, inspector, _res, _| {
265 let trace = TransactionOpcodeGas {
266 transaction_hash: tx_hash,
267 opcode_gas: inspector.opcode_gas_iter().collect(),
268 };
269 Ok(trace)
270 },
271 )
272 .await
273 }
274
275 fn calculate_base_block_reward<H: BlockHeader>(
280 &self,
281 header: &H,
282 ) -> Result<Option<u128>, Eth::Error> {
283 let chain_spec = self.provider().chain_spec();
284
285 if chain_spec.is_paris_active_at_block(header.number()) {
286 return Ok(None)
287 }
288
289 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
290 }
291
292 fn extract_reward_traces<H: BlockHeader>(
296 &self,
297 header: &H,
298 block_hash: BlockHash,
299 ommers: Option<&[H]>,
300 base_block_reward: u128,
301 ) -> Vec<LocalizedTransactionTrace> {
302 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
303 let mut traces = Vec::with_capacity(ommers_cnt + 1);
304
305 let block_reward = block_reward(base_block_reward, ommers_cnt);
306 traces.push(reward_trace(
307 block_hash,
308 header,
309 RewardAction {
310 author: header.beneficiary(),
311 reward_type: RewardType::Block,
312 value: U256::from(block_reward),
313 },
314 ));
315
316 let Some(ommers) = ommers else { return traces };
317
318 for uncle in ommers {
319 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
320 traces.push(reward_trace(
321 block_hash,
322 header,
323 RewardAction {
324 author: uncle.beneficiary(),
325 reward_type: RewardType::Uncle,
326 value: U256::from(uncle_reward),
327 },
328 ));
329 }
330 traces
331 }
332}
333
334impl<Eth> TraceApi<Eth>
335where
336 Eth: TraceExt + 'static,
339{
340 pub async fn trace_filter(
345 &self,
346 filter: TraceFilter,
347 ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
348 let matcher = Arc::new(filter.matcher());
350 let TraceFilter { from_block, to_block, mut after, count, .. } = filter;
351 let start = from_block.unwrap_or(0);
352
353 let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
354 if start > latest_block {
355 return Err(EthApiError::HeaderNotFound(start.into()).into());
357 }
358 let end = to_block.unwrap_or(latest_block);
359 if end > latest_block {
360 return Err(EthApiError::HeaderNotFound(end.into()).into());
361 }
362
363 if start > end {
364 return Err(EthApiError::InvalidParams(
365 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
366 )
367 .into())
368 }
369
370 let distance = end.saturating_sub(start);
372 if distance > self.inner.eth_config.max_trace_filter_blocks {
373 return Err(EthApiError::InvalidParams(format!(
374 "Block range too large; currently limited to {} blocks",
375 self.inner.eth_config.max_trace_filter_blocks
376 ))
377 .into())
378 }
379
380 let mut all_traces = Vec::new();
381 let mut block_traces = Vec::with_capacity(self.inner.eth_config.max_tracing_requests);
382 for chunk_start in (start..=end).step_by(self.inner.eth_config.max_tracing_requests) {
383 let chunk_end = std::cmp::min(
384 chunk_start + self.inner.eth_config.max_tracing_requests as u64 - 1,
385 end,
386 );
387
388 let blocks = self
390 .eth_api()
391 .spawn_blocking_io(move |this| {
392 Ok(this
393 .provider()
394 .recovered_block_range(chunk_start..=chunk_end)
395 .map_err(Eth::Error::from_eth_err)?
396 .into_iter()
397 .map(Arc::new)
398 .collect::<Vec<_>>())
399 })
400 .await?;
401
402 for block in &blocks {
404 let matcher = matcher.clone();
405 let traces = self.eth_api().trace_block_until(
406 block.hash().into(),
407 Some(block.clone()),
408 None,
409 TracingInspectorConfig::default_parity(),
410 move |tx_info, mut ctx| {
411 let mut traces = ctx
412 .take_inspector()
413 .into_parity_builder()
414 .into_localized_transaction_traces(tx_info);
415 traces.retain(|trace| matcher.matches(&trace.trace));
416 Ok(Some(traces))
417 },
418 );
419 block_traces.push(traces);
420 }
421
422 #[expect(clippy::iter_with_drain)]
423 let block_traces = futures::future::try_join_all(block_traces.drain(..)).await?;
424 all_traces.extend(block_traces.into_iter().flatten().flat_map(|traces| {
425 traces.into_iter().flatten().flat_map(|traces| traces.into_iter())
426 }));
427
428 for block in &blocks {
430 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
431 all_traces.extend(
432 self.extract_reward_traces(
433 block.header(),
434 block.hash(),
435 block.body().ommers(),
436 base_block_reward,
437 )
438 .into_iter()
439 .filter(|trace| matcher.matches(&trace.trace)),
440 );
441 } else {
442 break
445 }
446 }
447
448 if let Some(cutoff) = after.map(|a| a as usize) &&
450 cutoff < all_traces.len()
451 {
452 all_traces.drain(..cutoff);
453 after = None;
455 }
456
457 if after.is_none() &&
459 let Some(count) = count
460 {
461 let count = count as usize;
462 if count < all_traces.len() {
463 all_traces.truncate(count);
464 return Ok(all_traces)
465 }
466 }
467 }
468
469 if let Some(cutoff) = after.map(|a| a as usize) &&
472 cutoff >= all_traces.len()
473 {
474 return Ok(vec![])
475 }
476
477 Ok(all_traces)
478 }
479
480 pub async fn trace_block(
482 &self,
483 block_id: BlockId,
484 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
485 let Some(block) = self.eth_api().recovered_block(block_id).await? else {
486 return Err(EthApiError::HeaderNotFound(block_id).into());
487 };
488
489 let mut traces = self
490 .eth_api()
491 .trace_block_with(
492 block_id,
493 Some(block.clone()),
494 TracingInspectorConfig::default_parity(),
495 |tx_info, mut ctx| {
496 let traces = ctx
497 .take_inspector()
498 .into_parity_builder()
499 .into_localized_transaction_traces(tx_info);
500 Ok(traces)
501 },
502 )
503 .await?
504 .map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
505
506 if let Some(traces) = traces.as_mut() &&
507 let Some(base_block_reward) = self.calculate_base_block_reward(block.header())?
508 {
509 traces.extend(self.extract_reward_traces(
510 block.header(),
511 block.hash(),
512 block.body().ommers(),
513 base_block_reward,
514 ));
515 }
516
517 Ok(traces)
518 }
519
520 pub async fn replay_block_transactions(
522 &self,
523 block_id: BlockId,
524 trace_types: HashSet<TraceType>,
525 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
526 self.eth_api()
527 .trace_block_with(
528 block_id,
529 None,
530 TracingInspectorConfig::from_parity_config(&trace_types),
531 move |tx_info, mut ctx| {
532 let mut full_trace = ctx
533 .take_inspector()
534 .into_parity_builder()
535 .into_trace_results(&ctx.result, &trace_types);
536
537 if let Some(ref mut state_diff) = full_trace.state_diff {
540 populate_state_diff(state_diff, &ctx.db, ctx.state.iter())
541 .map_err(Eth::Error::from_eth_err)?;
542 }
543
544 let trace = TraceResultsWithTransactionHash {
545 transaction_hash: tx_info.hash.expect("tx hash is set"),
546 full_trace,
547 };
548 Ok(trace)
549 },
550 )
551 .await
552 }
553
554 pub async fn trace_block_opcode_gas(
559 &self,
560 block_id: BlockId,
561 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
562 let Some(block) = self.eth_api().recovered_block(block_id).await? else {
563 return Err(EthApiError::HeaderNotFound(block_id).into());
564 };
565
566 let Some(transactions) = self
567 .eth_api()
568 .trace_block_inspector(
569 block_id,
570 Some(block.clone()),
571 OpcodeGasInspector::default,
572 move |tx_info, ctx| {
573 let trace = TransactionOpcodeGas {
574 transaction_hash: tx_info.hash.expect("tx hash is set"),
575 opcode_gas: ctx.inspector.opcode_gas_iter().collect(),
576 };
577 Ok(trace)
578 },
579 )
580 .await?
581 else {
582 return Ok(None);
583 };
584
585 Ok(Some(BlockOpcodeGas {
586 block_hash: block.hash(),
587 block_number: block.number(),
588 transactions,
589 }))
590 }
591
592 pub async fn trace_block_storage_access(
595 &self,
596 block_id: BlockId,
597 ) -> Result<Option<BlockStorageAccess>, Eth::Error> {
598 let Some(block) = self.eth_api().recovered_block(block_id).await? else {
599 return Err(EthApiError::HeaderNotFound(block_id).into());
600 };
601
602 let Some(transactions) = self
603 .eth_api()
604 .trace_block_inspector(
605 block_id,
606 Some(block.clone()),
607 StorageInspector::default,
608 move |tx_info, mut ctx| {
609 let unique_loads = ctx.inspector.unique_loads();
610 let warm_loads = ctx.inspector.warm_loads();
611 let trace = TransactionStorageAccess {
612 transaction_hash: tx_info.hash.expect("tx hash is set"),
613 storage_access: ctx.take_inspector().into_accessed_slots(),
614 unique_loads,
615 warm_loads,
616 };
617 Ok(trace)
618 },
619 )
620 .await?
621 else {
622 return Ok(None);
623 };
624
625 Ok(Some(BlockStorageAccess {
626 block_hash: block.hash(),
627 block_number: block.number(),
628 transactions,
629 }))
630 }
631}
632
633#[async_trait]
634impl<Eth> TraceApiServer<RpcTxReq<Eth::NetworkTypes>> for TraceApi<Eth>
635where
636 Eth: TraceExt + 'static,
637{
638 async fn trace_call(
642 &self,
643 call: RpcTxReq<Eth::NetworkTypes>,
644 trace_types: HashSet<TraceType>,
645 block_id: Option<BlockId>,
646 state_overrides: Option<StateOverride>,
647 block_overrides: Option<Box<BlockOverrides>>,
648 ) -> RpcResult<TraceResults> {
649 let _permit = self.acquire_trace_permit().await;
650 let request =
651 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
652 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
653 }
654
655 async fn trace_call_many(
657 &self,
658 calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
659 block_id: Option<BlockId>,
660 ) -> RpcResult<Vec<TraceResults>> {
661 let _permit = self.acquire_trace_permit().await;
662 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
663 }
664
665 async fn trace_raw_transaction(
667 &self,
668 data: Bytes,
669 trace_types: HashSet<TraceType>,
670 block_id: Option<BlockId>,
671 ) -> RpcResult<TraceResults> {
672 let _permit = self.acquire_trace_permit().await;
673 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
674 .await
675 .map_err(Into::into)?)
676 }
677
678 async fn replay_block_transactions(
680 &self,
681 block_id: BlockId,
682 trace_types: HashSet<TraceType>,
683 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
684 let _permit = self.acquire_trace_permit().await;
685 Ok(Self::replay_block_transactions(self, block_id, trace_types)
686 .await
687 .map_err(Into::into)?)
688 }
689
690 async fn replay_transaction(
692 &self,
693 transaction: B256,
694 trace_types: HashSet<TraceType>,
695 ) -> RpcResult<TraceResults> {
696 let _permit = self.acquire_trace_permit().await;
697 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
698 }
699
700 async fn trace_block(
702 &self,
703 block_id: BlockId,
704 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
705 let _permit = self.acquire_trace_permit().await;
706 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
707 }
708
709 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
716 let _permit = self.inner.blocking_task_guard.clone().acquire_many_owned(2).await;
717 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
718 }
719
720 async fn trace_get(
723 &self,
724 hash: B256,
725 indices: Vec<Index>,
726 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
727 let _permit = self.acquire_trace_permit().await;
728 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
729 .await
730 .map_err(Into::into)?)
731 }
732
733 async fn trace_transaction(
735 &self,
736 hash: B256,
737 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
738 let _permit = self.acquire_trace_permit().await;
739 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
740 }
741
742 async fn trace_transaction_opcode_gas(
744 &self,
745 tx_hash: B256,
746 ) -> RpcResult<Option<TransactionOpcodeGas>> {
747 let _permit = self.acquire_trace_permit().await;
748 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
749 }
750
751 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
753 let _permit = self.acquire_trace_permit().await;
754 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
755 }
756}
757
758impl<Eth> std::fmt::Debug for TraceApi<Eth> {
759 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
760 f.debug_struct("TraceApi").finish_non_exhaustive()
761 }
762}
763impl<Eth> Clone for TraceApi<Eth> {
764 fn clone(&self) -> Self {
765 Self { inner: Arc::clone(&self.inner) }
766 }
767}
768
769struct TraceApiInner<Eth> {
770 eth_api: Eth,
772 blocking_task_guard: BlockingTaskGuard,
774 eth_config: EthConfig,
776}
777
778#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
781#[serde(rename_all = "camelCase")]
782pub struct TransactionStorageAccess {
783 pub transaction_hash: B256,
785 pub storage_access: HashMap<Address, HashMap<B256, u64>>,
787 pub unique_loads: u64,
789 pub warm_loads: u64,
791}
792
793#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
795#[serde(rename_all = "camelCase")]
796pub struct BlockStorageAccess {
797 pub block_hash: BlockHash,
799 pub block_number: u64,
801 pub transactions: Vec<TransactionStorageAccess>,
803}
804
805fn reward_trace<H: BlockHeader>(
808 block_hash: BlockHash,
809 header: &H,
810 reward: RewardAction,
811) -> LocalizedTransactionTrace {
812 LocalizedTransactionTrace {
813 block_hash: Some(block_hash),
814 block_number: Some(header.number()),
815 transaction_hash: None,
816 transaction_position: None,
817 trace: TransactionTrace {
818 trace_address: vec![],
819 subtraces: 0,
820 action: Action::Reward(reward),
821 error: None,
822 result: None,
823 },
824 }
825}