diff --git a/Cargo.lock b/Cargo.lock index 9219e50eb2d..0bc9f38389d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2292,7 +2292,6 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.90", - "unicode-xid", ] [[package]] @@ -2489,18 +2488,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "educe" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "either" version = "1.13.0" @@ -2612,26 +2599,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "enum-ordinalize" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" -dependencies = [ - "enum-ordinalize-derive", -] - -[[package]] -name = "enum-ordinalize-derive" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -3163,20 +3130,24 @@ dependencies = [ [[package]] name = "fuel-asm" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f325971bf9047ec70004f80a989e03456316bc19cbef3ff3a39a38b192ab56e" dependencies = [ "bitflags 2.6.0", - "fuel-types 0.59.0", + "fuel-types 0.58.2", "serde", "strum 0.24.1", ] [[package]] name = "fuel-compression" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e42841f56f76ed759b3f516e5188d5c42de47015bee951651660c13b6dfa6c" dependencies = [ - "fuel-derive 0.59.0", - "fuel-types 0.59.0", + "fuel-derive 0.58.2", + "fuel-types 0.58.2", "serde", ] @@ -3687,7 +3658,7 @@ dependencies = [ "enum-iterator", "fuel-core-storage", "fuel-core-types 0.40.0", - "fuel-vm 0.59.0", + "fuel-vm 0.58.2", "impl-tools", "itertools 0.12.1", "mockall", @@ -3829,11 +3800,10 @@ dependencies = [ "bs58", "derivative", "derive_more 0.99.18", - "fuel-vm 0.59.0", + "fuel-vm 0.58.2", "rand", "secrecy", "serde", - "serde-big-array", "tai64", "zeroize", ] @@ -3887,13 +3857,15 @@ dependencies = [ [[package]] name = "fuel-crypto" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e318850ca64890ff123a99b6b866954ef49da94ab9bc6827cf6ee045568585" dependencies = [ "coins-bip32", "coins-bip39", "ecdsa", "ed25519-dalek", - "fuel-types 0.59.0", + "fuel-types 0.58.2", "k256", "lazy_static", "p256", @@ -3918,7 +3890,9 @@ dependencies = [ [[package]] name = "fuel-derive" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0bc46a3552964bae5169e79b383761a54bd115ea66951a1a7a229edcefa55a" dependencies = [ "proc-macro2", "quote", @@ -3954,11 +3928,13 @@ dependencies = [ [[package]] name = "fuel-merkle" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79eca6a452311c70978a5df796c0f99f27e474b69719e0db4c1d82e68800d07" dependencies = [ "derive_more 0.99.18", "digest 0.10.7", - "fuel-storage 0.59.0", + "fuel-storage 0.58.2", "hashbrown 0.13.2", "hex", "serde", @@ -3973,7 +3949,9 @@ checksum = "4c1b711f28553ddc5f3546711bd220e144ce4c1af7d9e9a1f70b2f20d9f5b791" [[package]] name = "fuel-storage" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0c46b5d76b3e11197bd31e036cd8b1cb46c4d822cacc48836638080c6d2b76" [[package]] name = "fuel-tx" @@ -3999,16 +3977,18 @@ dependencies = [ [[package]] name = "fuel-tx" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6723bb8710ba2b70516ac94d34459593225870c937670fb3afaf82e0354667ac" dependencies = [ "bitflags 2.6.0", - "derive_more 1.0.0", - "educe", - "fuel-asm 0.59.0", + "derivative", + "derive_more 0.99.18", + "fuel-asm 0.58.2", "fuel-compression", - "fuel-crypto 0.59.0", - "fuel-merkle 0.59.0", - "fuel-types 0.59.0", + "fuel-crypto 0.58.2", + "fuel-merkle 0.58.2", + "fuel-types 0.58.2", "hashbrown 0.14.5", "itertools 0.10.5", "postcard", @@ -4031,9 +4011,11 @@ dependencies = [ [[package]] name = "fuel-types" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982265415a99b5bd6277bc24194a233bb2e18764df11c937b3dbb11a02c9e545" dependencies = [ - "fuel-derive 0.59.0", + "fuel-derive 0.58.2", "hex", "rand", "serde", @@ -4072,22 +4054,24 @@ dependencies = [ [[package]] name = "fuel-vm" -version = "0.59.0" +version = "0.58.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b5362d7d072c72eec20581f67fc5400090c356a7f3ae77c79880b3b177b667" dependencies = [ "anyhow", "async-trait", "backtrace", "bitflags 2.6.0", + "derivative", "derive_more 0.99.18", - "educe", "ethnum", - "fuel-asm 0.59.0", + "fuel-asm 0.58.2", "fuel-compression", - "fuel-crypto 0.59.0", - "fuel-merkle 0.59.0", - "fuel-storage 0.59.0", - "fuel-tx 0.59.0", - "fuel-types 0.59.0", + "fuel-crypto 0.58.2", + "fuel-merkle 0.58.2", + "fuel-storage 0.58.2", + "fuel-tx 0.58.2", + "fuel-types 0.58.2", "hashbrown 0.14.5", "itertools 0.10.5", "libm", @@ -4100,7 +4084,6 @@ dependencies = [ "sha3", "static_assertions", "strum 0.24.1", - "substrate-bn", "tai64", ] @@ -5421,9 +5404,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] [[package]] name = "lazycell" @@ -8511,15 +8491,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-big-array" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.215" @@ -9001,19 +8972,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", -] - [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index bcb0108b548..05f8048b908 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,9 +87,7 @@ fuel-core-xtask = { version = "0.0.0", path = "./xtask" } fuel-gas-price-algorithm = { version = "0.40.0", path = "crates/fuel-gas-price-algorithm" } # Fuel dependencies -fuel-vm-private = { path = "../fuel-vm/fuel-vm", package = "fuel-vm", default-features = false } -# fuel-vm-private = { git = "https://github.com/FuelLabs/fuel-vm", branch = "dento/execution-trace", package = "fuel-vm", default-features = false } -# fuel-vm-private = { version = "0.58.2", package = "fuel-vm", default-features = false } +fuel-vm-private = { version = "0.58.2", package = "fuel-vm", default-features = false } # Common dependencies anyhow = "1.0" diff --git a/bin/fuel-core/src/cli/run.rs b/bin/fuel-core/src/cli/run.rs index 485f0ee740d..49bc0c94750 100644 --- a/bin/fuel-core/src/cli/run.rs +++ b/bin/fuel-core/src/cli/run.rs @@ -543,7 +543,7 @@ impl Command { get_peers: graphql.costs.get_peers, estimate_predicates: graphql.costs.estimate_predicates, dry_run: graphql.costs.dry_run, - execution_trace_block: graphql.costs.execution_trace_block, + storage_read_replay: graphql.costs.storage_read_replay, submit: graphql.costs.submit, submit_and_await: graphql.costs.submit_and_await, status_change: graphql.costs.status_change, diff --git a/bin/fuel-core/src/cli/run/graphql.rs b/bin/fuel-core/src/cli/run/graphql.rs index 78e4933c7c7..57c511c7a63 100644 --- a/bin/fuel-core/src/cli/run/graphql.rs +++ b/bin/fuel-core/src/cli/run/graphql.rs @@ -114,11 +114,11 @@ pub struct QueryCosts { /// Query costs for generating execution trace for a block.® #[clap( - long = "query-cost-execution-trace-block", - default_value = DEFAULT_QUERY_COSTS.execution_trace_block.to_string(), + long = "query-cost-storage-read-replay", + default_value = DEFAULT_QUERY_COSTS.storage_read_replay.to_string(), env )] - pub execution_trace_block: usize, + pub storage_read_replay: usize, /// Query costs for submitting a transaction. #[clap( diff --git a/crates/client/assets/schema.sdl b/crates/client/assets/schema.sdl index b3805d795ec..dd42c07b0fa 100644 --- a/crates/client/assets/schema.sdl +++ b/crates/client/assets/schema.sdl @@ -736,7 +736,7 @@ type Mutation { """ Get execution trace for an already-executed transaction. """ - executionTraceBlock(height: U32!, trigger: TraceTrigger!): [TraceTransactionExecutionStatus!]! + storageReadReplay(height: U32!): [[StorageReadReplayEvent!]!]! """ Submits transaction to the `TxPool`. @@ -1122,6 +1122,12 @@ type StateTransitionPurpose { root: Bytes32! } +type StorageReadReplayEvent { + column: String! + key: HexString! + value: HexString +} + type SubmittedStatus { time: Tai64Timestamp! @@ -1174,71 +1180,6 @@ type SuccessStatus { scalar Tai64Timestamp -type TraceFailureStatus { - programState: ProgramState - reason: String! - receipts: [Receipt!]! - executionTrace: [TraceFrame!]! - totalGas: U64! - totalFee: U64! -} - -type TraceFrame { - """ - Register values - """ - registers: [U64!]! - """ - Changes to memory `(address, bytes)` that occurred since the last frame - """ - memoryDiff: [TraceFrameMemoryPatch!]! - """ - How many of the original receipts have been produced by this point - """ - receiptCount: Int! -} - -type TraceFrameMemoryPatch { - """ - Start address - """ - start: Int! - """ - Bytes of data - """ - bytes: HexString! -} - -type TraceSuccessStatus { - programState: ProgramState - receipts: [Receipt!]! - executionTrace: [TraceFrame!]! - totalGas: U64! - totalFee: U64! -} - -type TraceTransactionExecutionStatus { - id: TransactionId! - status: TraceTransactionStatus! - receipts: [Receipt!]! -} - -union TraceTransactionStatus = TraceSuccessStatus | TraceFailureStatus - -""" -One of the films in the Star Wars Trilogy -""" -enum TraceTrigger { - """ - After each instruction - """ - ON_INSTRUCTION - """ - After an instruction has created a receipt - """ - ON_RECEIPT -} - type Transaction { id: TransactionId! inputAssetIds: [AssetId!] diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 941740f88c2..e14827e6a38 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -65,7 +65,7 @@ use fuel_core_types::{ Nonce, }, services::executor::{ - TraceTrigger, + StorageReadReplayEvent, TransactionExecutionStatus, }, }; @@ -509,26 +509,26 @@ impl FuelClient { .collect() } - /// Execution trace for a block - pub async fn execution_trace_block( + /// Get storage read replay for a block + pub async fn storage_read_replay( &self, height: &BlockHeight, - trigger: TraceTrigger, - ) -> io::Result> { + ) -> io::Result>> { let query: Operation< - schema::execution_trace::ExectionTraceBlock, - schema::execution_trace::ExectionTraceBlockArgs, - > = schema::execution_trace::ExectionTraceBlock::build( - schema::execution_trace::ExectionTraceBlockArgs { + schema::storage_read_replay::StorageReadReplay, + schema::storage_read_replay::StorageReadReplayArgs, + > = schema::storage_read_replay::StorageReadReplay::build( + schema::storage_read_replay::StorageReadReplayArgs { height: (*height).into(), - trigger: trigger.into(), }, ); - let tx_statuses = self.query(query).await.map(|r| r.execution_trace_block)?; - tx_statuses + Ok(self + .query(query) + .await + .map(|r| r.storage_read_replay)? .into_iter() - .map(|tx_status| tx_status.try_into().map_err(Into::into)) - .collect() + .map(|events| events.into_iter().map(Into::into).collect()) + .collect()) } /// Estimate predicates for the transaction diff --git a/crates/client/src/client/schema.rs b/crates/client/src/client/schema.rs index d28fb73f773..5d758bd5a39 100644 --- a/crates/client/src/client/schema.rs +++ b/crates/client/src/client/schema.rs @@ -33,9 +33,9 @@ pub mod chain; pub mod coins; pub mod contract; pub mod da_compressed; -pub mod execution_trace; pub mod message; pub mod node_info; +pub mod storage_read_replay; pub mod upgrades; pub mod gas_price; diff --git a/crates/client/src/client/schema/execution_trace.rs b/crates/client/src/client/schema/execution_trace.rs deleted file mode 100644 index 6bb090af8c2..00000000000 --- a/crates/client/src/client/schema/execution_trace.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::client::{ - schema::{ - schema, - tx::transparent_receipt::Receipt, - Address, - ConversionError, - TransactionId, - U32, - U64, - }, - PageDirection, - PaginationRequest, -}; -use fuel_core_types::{ - fuel_tx, - services::executor::{ - TransactionExecutionResult, - TransactionExecutionStatus, - }, -}; -use std::convert::{ - TryFrom, - TryInto, -}; - -use super::tx::ProgramState; - -#[allow(clippy::enum_variant_names)] -#[derive(cynic::InlineFragments, Clone, Debug)] -#[cynic(schema_path = "./assets/schema.sdl")] -pub enum TraceTransactionStatus { - SuccessStatus(TraceSuccessStatus), - FailureStatus(TraceFailureStatus), - #[cynic(fallback)] - Unknown, -} - -impl TryFrom for TransactionExecutionResult { - type Error = ConversionError; - - fn try_from(status: TraceTransactionStatus) -> Result { - Ok(match status { - TraceTransactionStatus::SuccessStatus(s) => { - let receipts = s - .receipts - .into_iter() - .map(|receipt| receipt.try_into()) - .collect::, _>>()?; - TransactionExecutionResult::Success { - result: s.program_state.map(TryInto::try_into).transpose()?, - receipts, - execution_trace: Vec::new(), // Not produced by dry-run - total_gas: s.total_gas.0, - total_fee: s.total_fee.0, - } - } - TraceTransactionStatus::FailureStatus(s) => { - let receipts = s - .receipts - .into_iter() - .map(|receipt| receipt.try_into()) - .collect::, _>>()?; - TransactionExecutionResult::Failed { - result: s.program_state.map(TryInto::try_into).transpose()?, - receipts, - execution_trace: Vec::new(), // Not produced by dry-run - total_gas: s.total_gas.0, - total_fee: s.total_fee.0, - } - } - TraceTransactionStatus::Unknown => { - return Err(Self::Error::UnknownVariant("ExecutionTraceTxStatus")) - } - }) - } -} - -#[derive(cynic::QueryFragment, Clone, Debug)] -#[cynic(schema_path = "./assets/schema.sdl")] -pub struct TraceSuccessStatus { - pub program_state: Option, - pub receipts: Vec, - pub total_gas: U64, - pub total_fee: U64, -} - -#[derive(cynic::QueryFragment, Clone, Debug)] -#[cynic(schema_path = "./assets/schema.sdl")] -pub struct TraceFailureStatus { - pub program_state: Option, - pub receipts: Vec, - pub total_gas: U64, - pub total_fee: U64, -} - -#[derive(cynic::QueryFragment, Clone, Debug)] -#[cynic(schema_path = "./assets/schema.sdl")] -pub struct TraceTransactionExecutionStatus { - pub id: TransactionId, - pub status: TraceTransactionStatus, -} - -impl TryFrom for TransactionExecutionStatus { - type Error = ConversionError; - - fn try_from(schema: TraceTransactionExecutionStatus) -> Result { - let id = schema.id.into(); - let status = schema.status.try_into()?; - - Ok(TransactionExecutionStatus { id, result: status }) - } -} - -#[derive(cynic::QueryVariables, Debug)] -pub struct TransactionsByOwnerConnectionArgs { - /// Select transactions based on related `owner`s - pub owner: Address, - /// Skip until cursor (forward pagination) - pub after: Option, - /// Skip until cursor (backward pagination) - pub before: Option, - /// Retrieve the first n transactions in order (forward pagination) - pub first: Option, - /// Retrieve the last n transactions in order (backward pagination). - /// Can't be used at the same time as `first`. - pub last: Option, -} - -impl From<(Address, PaginationRequest)> for TransactionsByOwnerConnectionArgs { - fn from(r: (Address, PaginationRequest)) -> Self { - match r.1.direction { - PageDirection::Forward => TransactionsByOwnerConnectionArgs { - owner: r.0, - after: r.1.cursor, - before: None, - first: Some(r.1.results), - last: None, - }, - PageDirection::Backward => TransactionsByOwnerConnectionArgs { - owner: r.0, - after: None, - before: r.1.cursor, - first: None, - last: Some(r.1.results), - }, - } - } -} - -// mutations - -/// When to record a trace frames during execution -#[derive(cynic::Enum, Debug, Copy, Clone, Eq, PartialEq)] -#[cynic(schema_path = "./assets/schema.sdl", graphql_type = "TraceTrigger")] -pub enum TraceTrigger { - /// After each instruction - OnInstruction, - /// After an instruction has created a receipt - OnReceipt, -} -impl From for TraceTrigger { - fn from(trigger: fuel_core_types::services::executor::TraceTrigger) -> Self { - match trigger { - fuel_core_types::services::executor::TraceTrigger::OnInstruction => { - TraceTrigger::OnInstruction - } - fuel_core_types::services::executor::TraceTrigger::OnReceipt => { - TraceTrigger::OnReceipt - } - } - } -} - -#[derive(cynic::QueryVariables, Debug)] -pub struct ExectionTraceBlockArgs { - pub height: U32, - pub trigger: TraceTrigger, -} - -/// Retrieves the transaction in opaque form -#[derive(cynic::QueryFragment, Clone, Debug)] -#[cynic( - schema_path = "./assets/schema.sdl", - graphql_type = "Mutation", - variables = "ExectionTraceBlockArgs" -)] -pub struct ExectionTraceBlock { - #[arguments(height: $height, trigger: $trigger)] - pub execution_trace_block: Vec, -} - -#[cfg(test)] -pub mod tests { - use super::*; - use fuel_core_types::fuel_types::BlockHeight; - - #[test] - fn execution_trace_block_tx_gql_output() { - use cynic::MutationBuilder; - let query = ExectionTraceBlock::build(ExectionTraceBlockArgs { - height: BlockHeight::new(1234).into(), - trigger: TraceTrigger::OnReceipt, - }); - insta::assert_snapshot!(query.query) - } -} diff --git a/crates/client/src/client/schema/storage_read_replay.rs b/crates/client/src/client/schema/storage_read_replay.rs new file mode 100644 index 00000000000..075f4952bb9 --- /dev/null +++ b/crates/client/src/client/schema/storage_read_replay.rs @@ -0,0 +1,58 @@ +use super::HexString; +use crate::client::schema::{ + schema, + U32, +}; + +#[derive(cynic::QueryFragment, Clone, Debug)] +#[cynic(schema_path = "./assets/schema.sdl")] +pub struct StorageReadReplayEvent { + pub column: String, + pub key: HexString, + pub value: Option, +} +impl From + for fuel_core_types::services::executor::StorageReadReplayEvent +{ + fn from(event: StorageReadReplayEvent) -> Self { + fuel_core_types::services::executor::StorageReadReplayEvent { + column: event.column, + key: event.key.into(), + value: event.value.map(Into::into), + } + } +} + +// mutations + +#[derive(cynic::QueryVariables, Debug)] +pub struct StorageReadReplayArgs { + pub height: U32, +} + +/// Retrieves the transaction in opaque form +#[derive(cynic::QueryFragment, Clone, Debug)] +#[cynic( + schema_path = "./assets/schema.sdl", + graphql_type = "Mutation", + variables = "StorageReadReplayArgs" +)] +pub struct StorageReadReplay { + #[arguments(height: $height)] + pub storage_read_replay: Vec>, +} + +#[cfg(test)] +pub mod tests { + use super::*; + use fuel_core_types::fuel_types::BlockHeight; + + #[test] + fn execution_trace_block_tx_gql_output() { + use cynic::MutationBuilder; + let query = StorageReadReplay::build(StorageReadReplayArgs { + height: BlockHeight::new(1234).into(), + }); + insta::assert_snapshot!(query.query) + } +} diff --git a/crates/client/src/client/schema/tx.rs b/crates/client/src/client/schema/tx.rs index 144f63b1992..f67152f9db3 100644 --- a/crates/client/src/client/schema/tx.rs +++ b/crates/client/src/client/schema/tx.rs @@ -292,7 +292,6 @@ impl TryFrom for TransactionExecutionResult { TransactionExecutionResult::Success { result: s.program_state.map(TryInto::try_into).transpose()?, receipts, - execution_trace: Vec::new(), // Not produced by dry-run total_gas: s.total_gas.0, total_fee: s.total_fee.0, } @@ -306,7 +305,6 @@ impl TryFrom for TransactionExecutionResult { TransactionExecutionResult::Failed { result: s.program_state.map(TryInto::try_into).transpose()?, receipts, - execution_trace: Vec::new(), // Not produced by dry-run total_gas: s.total_gas.0, total_fee: s.total_fee.0, } diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index bf7cf4011ee..313cfc7b43f 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -56,7 +56,7 @@ pub struct Costs { pub get_peers: usize, pub estimate_predicates: usize, pub dry_run: usize, - pub execution_trace_block: usize, + pub storage_read_replay: usize, pub submit: usize, pub submit_and_await: usize, pub status_change: usize, @@ -89,7 +89,7 @@ pub const DEFAULT_QUERY_COSTS: Costs = Costs { get_peers: 40001, estimate_predicates: 40001, dry_run: 12000, - execution_trace_block: 1_000_000, + storage_read_replay: 1_000_000, submit: 40001, submit_and_await: 40001, status_change: 40001, diff --git a/crates/fuel-core/src/graphql_api/database.rs b/crates/fuel-core/src/graphql_api/database.rs index 36f02562ca6..7ef9b976bf4 100644 --- a/crates/fuel-core/src/graphql_api/database.rs +++ b/crates/fuel-core/src/graphql_api/database.rs @@ -264,10 +264,9 @@ impl StorageRead for ReadView { fn read( &self, key: &BlobId, - offset: usize, buf: &mut [u8], ) -> Result, Self::Error> { - StorageRead::::read(self.on_chain.as_ref(), key, offset, buf) + StorageRead::::read(self.on_chain.as_ref(), key, buf) } fn read_alloc(&self, key: &BlobId) -> Result>, Self::Error> { diff --git a/crates/fuel-core/src/graphql_api/ports.rs b/crates/fuel-core/src/graphql_api/ports.rs index 7489ed2a213..57f1006f3a5 100644 --- a/crates/fuel-core/src/graphql_api/ports.rs +++ b/crates/fuel-core/src/graphql_api/ports.rs @@ -55,7 +55,10 @@ use fuel_core_types::{ }, fuel_vm::interpreter::Memory, services::{ - executor::TransactionExecutionStatus, + executor::{ + StorageReadReplayEvent, + TransactionExecutionStatus, + }, graphql_api::ContractBalance, p2p::PeerInfo, txpool::TransactionStatus, @@ -235,11 +238,10 @@ pub trait BlockProducerPort: Send + Sync { gas_price: Option, ) -> anyhow::Result>; - async fn execution_trace_block( + async fn storage_read_replay( &self, height: BlockHeight, - trigger: Trigger, - ) -> anyhow::Result>; + ) -> anyhow::Result>>; } #[async_trait::async_trait] diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index 17b7c464ea8..9acdbb01940 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -280,8 +280,7 @@ impl GasCosts { GasCostsValues::V1(_) | GasCostsValues::V2(_) | GasCostsValues::V3(_) - | GasCostsValues::V4(_) - | GasCostsValues::V5(_) => GasCostsVersion::V1, + | GasCostsValues::V4(_) => GasCostsVersion::V1, } } diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index d554c3aae00..7e920f3ead2 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -132,7 +132,7 @@ impl ConcreteStorage { let fee_params = params.fee_params(); let ready_tx = checked_tx - .into_ready(GAS_PRICE, gas_costs, fee_params, None) + .into_ready(GAS_PRICE, gas_costs, fee_params) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; @@ -176,7 +176,7 @@ impl ConcreteStorage { let fee_params = params.fee_params(); let ready_tx = checked_tx - .into_ready(GAS_PRICE, gas_costs, fee_params, None) + .into_ready(GAS_PRICE, gas_costs, fee_params) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; @@ -456,7 +456,7 @@ impl DapMutation { match checked_tx { CheckedTransaction::Script(script) => { let ready_tx = script - .into_ready(GAS_PRICE, gas_costs, fee_params, None) + .into_ready(GAS_PRICE, gas_costs, fee_params) .map_err(|e| { anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) })?; diff --git a/crates/fuel-core/src/schema/tx.rs b/crates/fuel-core/src/schema/tx.rs index f555afb966f..9a14aabb3cf 100644 --- a/crates/fuel-core/src/schema/tx.rs +++ b/crates/fuel-core/src/schema/tx.rs @@ -76,8 +76,7 @@ use std::{ }; use types::{ DryRunTransactionExecutionStatus, - TraceTransactionExecutionStatus, - TraceTrigger, + StorageReadReplayEvent, Transaction, }; @@ -338,20 +337,23 @@ impl TxMutation { /// Get execution trace for an already-executed transaction. #[graphql(complexity = "query_costs().dry_run + child_complexity")] - async fn execution_trace_block( + async fn storage_read_replay( &self, ctx: &Context<'_>, height: U32, - trigger: TraceTrigger, - ) -> async_graphql::Result> { + ) -> async_graphql::Result>> { let block_height = height.into(); let block_producer = ctx.data_unchecked::(); - let status = block_producer - .execution_trace_block(block_height, trigger.into()) - .await?; - Ok(status + Ok(block_producer + .storage_read_replay(block_height) + .await? .into_iter() - .map(TraceTransactionExecutionStatus) + .map(|items| { + items + .into_iter() + .map(StorageReadReplayEvent::from) + .collect() + }) .collect()) } diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index f333f087905..0d313930cd2 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -78,10 +78,7 @@ use fuel_core_types::{ TxId, }, fuel_types::canonical::Serialize, - fuel_vm::{ - consts::VM_REGISTER_COUNT, - ProgramState as VmProgramState, - }, + fuel_vm::ProgramState as VmProgramState, services::{ executor::{ TransactionExecutionResult, @@ -867,7 +864,6 @@ impl DryRunTransactionStatus { receipts, total_gas, total_fee, - execution_trace: _, // Discard, as dry run doesn't support this } => DryRunTransactionStatus::Success(DryRunSuccessStatus { result, receipts, @@ -879,7 +875,6 @@ impl DryRunTransactionStatus { receipts, total_gas, total_fee, - execution_trace: _, // Discard, as dry run doesn't support this } => DryRunTransactionStatus::Failed(DryRunFailureStatus { result, receipts, @@ -890,54 +885,6 @@ impl DryRunTransactionStatus { } } -/// When to record a trace frames during execution -#[derive(Enum, Debug, Copy, Clone, Eq, PartialEq)] -pub enum TraceTrigger { - /// After each instruction - OnInstruction, - /// After an instruction has created a receipt - OnReceipt, -} - -#[derive(Union, Debug)] -pub enum TraceTransactionStatus { - Success(TraceSuccessStatus), - Failed(TraceFailureStatus), -} - -impl TraceTransactionStatus { - pub fn new(tx_status: TransactionExecutionResult) -> Self { - match tx_status { - TransactionExecutionResult::Success { - result, - receipts, - total_gas, - total_fee, - execution_trace, - } => TraceTransactionStatus::Success(TraceSuccessStatus { - result, - receipts, - execution_trace, - total_gas, - total_fee, - }), - TransactionExecutionResult::Failed { - result, - receipts, - execution_trace, - total_gas, - total_fee, - } => TraceTransactionStatus::Failed(TraceFailureStatus { - result, - receipts, - execution_trace, - total_gas, - total_fee, - }), - } - } -} - fn input_contracts(tx: &Tx) -> IntoIter<&fuel_core_types::fuel_types::ContractId> where Tx: Inputs, @@ -1033,157 +980,36 @@ impl DryRunTransactionExecutionStatus { } } -#[derive(Debug)] -pub struct TraceFrame(Frame); - -impl From for TraceFrame { - fn from(frame: Frame) -> Self { - Self(frame) - } -} - -#[Object] -impl TraceFrame { - /// Register values - async fn registers(&self) -> [U64; VM_REGISTER_COUNT] { - self.0.registers.map(|r| r.into()) - } - - /// Changes to memory `(address, bytes)` that occurred since the last frame - async fn memory_diff(&self) -> Vec { - self.0 - .memory_diff - .clone() - .into_iter() - .map(|c| c.into()) - .collect() - } - - /// How many of the original receipts have been produced by this point - async fn receipt_count(&self) -> usize { - self.0.receipt_count - } +pub struct StorageReadReplayEvent { + column: String, + key: HexString, + value: Option, } -#[derive(Debug)] -pub struct TraceFrameMemoryPatch { - start: usize, - bytes: Vec, -} -impl From - for TraceFrameMemoryPatch +impl From + for StorageReadReplayEvent { - fn from(change: fuel_core_types::fuel_vm::interpreter::MemorySliceChange) -> Self { + fn from(event: fuel_core_types::services::executor::StorageReadReplayEvent) -> Self { Self { - start: change.global_start, - bytes: change.data, + column: event.column, + key: HexString(event.key), + value: event.value.map(HexString), } } } #[Object] -impl TraceFrameMemoryPatch { - /// Start address - async fn start(&self) -> u32 { - u32::try_from(self.start).expect("Invalid memory address") +impl StorageReadReplayEvent { + async fn column(&self) -> String { + self.column.clone() } - /// Bytes of data - async fn bytes(&self) -> HexString { - self.bytes.clone().into() + async fn key(&self) -> HexString { + self.key.clone() } -} - -#[derive(Debug)] -pub struct TraceSuccessStatus { - result: Option, - receipts: Vec, - execution_trace: Vec, - total_gas: u64, - total_fee: u64, -} -#[Object] -impl TraceSuccessStatus { - async fn program_state(&self) -> Option { - self.result.map(Into::into) - } - - async fn receipts(&self) -> Vec { - self.receipts.iter().map(Into::into).collect() - } - - async fn execution_trace(&self) -> Vec { - self.execution_trace - .clone() - .into_iter() - .map(TraceFrame::from) - .collect() - } - - async fn total_gas(&self) -> U64 { - self.total_gas.into() - } - - async fn total_fee(&self) -> U64 { - self.total_fee.into() - } -} - -#[derive(Debug)] -pub struct TraceFailureStatus { - result: Option, - receipts: Vec, - execution_trace: Vec, - total_gas: u64, - total_fee: u64, -} - -#[Object] -impl TraceFailureStatus { - async fn program_state(&self) -> Option { - self.result.map(Into::into) - } - - async fn reason(&self) -> String { - TransactionExecutionResult::reason(&self.receipts, &self.result) - } - - async fn receipts(&self) -> Vec { - self.receipts.iter().map(Into::into).collect() - } - - async fn execution_trace(&self) -> Vec { - self.execution_trace - .clone() - .into_iter() - .map(TraceFrame::from) - .collect() - } - - async fn total_gas(&self) -> U64 { - self.total_gas.into() - } - - async fn total_fee(&self) -> U64 { - self.total_fee.into() - } -} - -pub struct TraceTransactionExecutionStatus(pub TransactionExecutionStatus); - -#[Object] -impl TraceTransactionExecutionStatus { - async fn id(&self) -> TransactionId { - TransactionId(self.0.id) - } - - async fn status(&self) -> TraceTransactionStatus { - TraceTransactionStatus::new(self.0.result.clone()) - } - - async fn receipts(&self) -> Vec { - self.0.result.receipts().iter().map(Into::into).collect() + async fn value(&self) -> Option { + self.value.clone() } } diff --git a/crates/fuel-core/src/service/adapters/graphql_api.rs b/crates/fuel-core/src/service/adapters/graphql_api.rs index 1fd260fbc44..667a12b5cba 100644 --- a/crates/fuel-core/src/service/adapters/graphql_api.rs +++ b/crates/fuel-core/src/service/adapters/graphql_api.rs @@ -43,7 +43,10 @@ use fuel_core_types::{ fuel_types::BlockHeight, services::{ block_importer::SharedImportResult, - executor::TransactionExecutionStatus, + executor::{ + StorageReadReplayEvent, + TransactionExecutionStatus, + }, p2p::PeerInfo, txpool::TransactionStatus, }, @@ -124,14 +127,11 @@ impl BlockProducerPort for BlockProducerAdapter { .await } - async fn execution_trace_block( + async fn storage_read_replay( &self, height: BlockHeight, - trigger: Trigger, - ) -> anyhow::Result> { - self.block_producer - .execution_trace_block(height, trigger) - .await + ) -> anyhow::Result>> { + self.block_producer.storage_read_replay(height).await } } diff --git a/crates/fuel-core/src/service/adapters/producer.rs b/crates/fuel-core/src/service/adapters/producer.rs index 1ac4453d91b..d5763192957 100644 --- a/crates/fuel-core/src/service/adapters/producer.rs +++ b/crates/fuel-core/src/service/adapters/producer.rs @@ -64,6 +64,7 @@ use fuel_core_types::{ block_producer::Components, executor::{ Result as ExecutorResult, + StorageReadReplayEvent, TransactionExecutionStatus, UncommittedResult, }, @@ -128,13 +129,12 @@ impl fuel_core_producer::ports::DryRunner for ExecutorAdapter { } } -impl fuel_core_producer::ports::BlockExecutionTracer for ExecutorAdapter { - fn execution_trace( +impl fuel_core_producer::ports::StorageReadReplayRecorder for ExecutorAdapter { + fn storage_read_replay( &self, block: &Block, - trigger: Trigger, - ) -> ExecutorResult> { - self.executor.execution_traces(block, trigger) + ) -> ExecutorResult>> { + self.executor.storage_read_replay(block) } } diff --git a/crates/fuel-core/src/state/data_source.rs b/crates/fuel-core/src/state/data_source.rs index f30f3ee8268..cf107d0e1b9 100644 --- a/crates/fuel-core/src/state/data_source.rs +++ b/crates/fuel-core/src/state/data_source.rs @@ -67,10 +67,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.data.read(key, column, offset, buf) + self.data.read(key, column, buf) } } diff --git a/crates/fuel-core/src/state/generic_database.rs b/crates/fuel-core/src/state/generic_database.rs index 5a31591268f..b6f5f2ea464 100644 --- a/crates/fuel-core/src/state/generic_database.rs +++ b/crates/fuel-core/src/state/generic_database.rs @@ -79,13 +79,8 @@ where M: Mappable, StructuredStorage: StorageRead, { - fn read( - &self, - key: &M::Key, - offset: usize, - buf: &mut [u8], - ) -> Result, Self::Error> { - self.storage.storage::().read(key, offset, buf) + fn read(&self, key: &M::Key, buf: &mut [u8]) -> Result, Self::Error> { + self.storage.storage::().read(key, buf) } fn read_alloc(&self, key: &M::Key) -> Result>, Self::Error> { @@ -129,10 +124,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - KeyValueInspect::read(&self.storage, key, column, offset, buf) + KeyValueInspect::read(&self.storage, key, column, buf) } } diff --git a/crates/fuel-core/src/state/historical_rocksdb.rs b/crates/fuel-core/src/state/historical_rocksdb.rs index 059fb35f12b..a0bd39b2801 100644 --- a/crates/fuel-core/src/state/historical_rocksdb.rs +++ b/crates/fuel-core/src/state/historical_rocksdb.rs @@ -534,11 +534,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.db - .read(key, Column::OriginalColumn(column), offset, buf) + self.db.read(key, Column::OriginalColumn(column), buf) } } diff --git a/crates/fuel-core/src/state/iterable_key_value_view.rs b/crates/fuel-core/src/state/iterable_key_value_view.rs index 1d8fd02cbbb..40ab3483ede 100644 --- a/crates/fuel-core/src/state/iterable_key_value_view.rs +++ b/crates/fuel-core/src/state/iterable_key_value_view.rs @@ -67,10 +67,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.0.read(key, column, offset, buf) + self.0.read(key, column, buf) } } diff --git a/crates/fuel-core/src/state/key_value_view.rs b/crates/fuel-core/src/state/key_value_view.rs index e507e83329a..9e70037fc21 100644 --- a/crates/fuel-core/src/state/key_value_view.rs +++ b/crates/fuel-core/src/state/key_value_view.rs @@ -54,9 +54,8 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.0.read(key, column, offset, buf) + self.0.read(key, column, buf) } } diff --git a/crates/fuel-core/src/state/rocks_db.rs b/crates/fuel-core/src/state/rocks_db.rs index 7f0d31d05e2..e69c6289bed 100644 --- a/crates/fuel-core/src/state/rocks_db.rs +++ b/crates/fuel-core/src/state/rocks_db.rs @@ -758,7 +758,6 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, mut buf: &mut [u8], ) -> StorageResult> { self.metrics.read_meter.inc(); @@ -770,10 +769,8 @@ where .get_pinned_cf_opt(&self.cf(column), key, &self.read_options) .map_err(|e| DatabaseError::Other(e.into()))? .map(|value| { - let Some(read) = value.len().checked_sub(offset) else { - return Ok(0); - }; - std::io::Write::write_all(&mut buf, &value.as_ref()[offset..]) + let read = value.len(); + std::io::Write::write_all(&mut buf, value.as_ref()) .map_err(|e| DatabaseError::Other(anyhow::anyhow!(e)))?; StorageResult::Ok(read) }) diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index 915b10973af..a6b548ca024 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -5,10 +5,6 @@ use crate::{ TransactionsSource, }, refs::ContractRef, - trace::{ - TraceOnInstruction, - TraceOnReceipt, - }, }; use fuel_core_storage::{ column::Column, @@ -116,14 +112,11 @@ use fuel_core_types::{ IntoChecked, }, interpreter::{ - trace::ExecutionTraceHooks, CheckedMetadata as CheckedMetadataTrait, ExecutableTransaction, InterpreterParams, Memory, MemoryInstance, - NoTrace, - NotSupportedEcal, }, state::StateTransition, Backtrace as FuelBacktrace, @@ -138,8 +131,6 @@ use fuel_core_types::{ ExecutionResult, ForcedTransactionFailure, Result as ExecutorResult, - TraceFrame, - TraceTrigger, TransactionExecutionResult, TransactionExecutionStatus, TransactionValidityError, @@ -262,9 +253,6 @@ pub struct ExecutionOptions { pub extra_tx_checks: bool, /// Print execution backtraces if transaction execution reverts. pub backtrace: bool, - /// Record execution traces for each transaction with the given trigger - #[serde(default)] - pub execution_trace: Option, } /// The executor instance performs block production and validation. Given a block, it will execute all @@ -342,6 +330,19 @@ where Ok(UncommittedResult::new(result, changes)) } + pub fn record_storage_reads_for( + self, + block: &Block, + tx_separator_callback: &mut dyn FnMut(usize) -> (), + ) -> ExecutorResult> { + let consensus_params_version = block.header().consensus_parameters_version; + let (block_executor, storage_tx) = + self.into_executor(consensus_params_version)?; + Ok(block_executor + .record_storage_reads_for(block, storage_tx, tx_separator_callback)? + .tx_status) + } + pub fn validate_without_commit( self, block: &Block, @@ -714,6 +715,109 @@ where Ok(()) } + #[tracing::instrument(skip_all)] + fn record_storage_reads_for( + mut self, + block: &Block, + mut block_storage_tx: StorageTransaction, + tx_separator_callback: &mut dyn FnMut(usize) -> (), + ) -> ExecutorResult + where + D: KeyValueInspect, + { + let mut data = ExecutionData::new(); + + let partial_header = PartialBlockHeader::from(block.header()); + let mut partial_block = PartialFuelBlock::new(partial_header, vec![]); + let transactions = block.transactions(); + let mut memory = MemoryInstance::new(); + + let (gas_price, coinbase_contract_id) = + Self::get_coinbase_info_from_mint_tx(transactions)?; + + // self.process_l1_txs( + // &mut partial_block, + // coinbase_contract_id, + // &mut block_storage_tx, + // &mut data, + // &mut memory, + // )?; + + let block_height = *partial_block.header.height(); + let da_block_height = partial_block.header.da_height; + let relayed_txs = self.get_relayed_txs( + block_height, + da_block_height, + &mut data, + &mut block_storage_tx, + &mut memory, + )?; + + // self.process_relayed_txs( + // relayed_txs, + // block, + // storage_tx, + // data, + // coinbase_contract_id, + // memory, + // )?; + + let consensus_parameters_version = + partial_block.header.consensus_parameters_version; + let relayed_tx_iter = relayed_txs.into_iter(); + for (i, checked) in relayed_tx_iter.enumerate() { + tx_separator_callback(i); + let maybe_checked_transaction = MaybeCheckedTransaction::CheckedTransaction( + checked, + consensus_parameters_version, + ); + let tx_id = maybe_checked_transaction.id(&self.consensus_params.chain_id()); + match self.execute_transaction_and_commit( + &mut partial_block, + &mut block_storage_tx, + &mut data, + maybe_checked_transaction, + Self::RELAYED_GAS_PRICE, + coinbase_contract_id, + &mut memory, + ) { + Ok(_) => {} + Err(err) => { + let event = ExecutorEvent::ForcedTransactionFailed { + id: tx_id.into(), + block_height, + failure: err.to_string(), + }; + data.events.push(event); + } + } + } + + let processed_l1_tx_count = partial_block.transactions.len(); + + for (i, transaction) in + transactions.iter().enumerate().skip(processed_l1_tx_count) + { + tx_separator_callback(i); + let maybe_checked_tx = + MaybeCheckedTransaction::Transaction(transaction.clone()); + self.execute_transaction_and_commit( + &mut partial_block, + &mut block_storage_tx, + &mut data, + maybe_checked_tx, + gas_price, + coinbase_contract_id, + &mut memory, + )?; + } + + self.check_block_matches(partial_block, block, &data)?; + + data.changes = block_storage_tx.into_changes(); + Ok(data) + } + #[tracing::instrument(skip_all)] fn validate_block( mut self, @@ -1264,45 +1368,15 @@ where checked_tx = self.extra_tx_checks(checked_tx, header, storage_tx, memory)?; } - let (reverted, state, tx, receipts, trace_frames) = - match self.options.execution_trace { - Some(TraceTrigger::OnInstruction) => { - let (reverted, state, tx, receipts, trace) = self - .attempt_tx_execution_with_vm::<_, _, TraceOnInstruction>( - checked_tx, - header, - coinbase_contract_id, - gas_price, - storage_tx, - memory, - )?; - (reverted, state, tx, receipts, trace.frames) - } - Some(TraceTrigger::OnReceipt) => { - let (reverted, state, tx, receipts, trace) = self - .attempt_tx_execution_with_vm::<_, _, TraceOnReceipt>( - checked_tx, - header, - coinbase_contract_id, - gas_price, - storage_tx, - memory, - )?; - (reverted, state, tx, receipts, trace.frames) - } - None => { - let (reverted, state, tx, receipts, _) = self - .attempt_tx_execution_with_vm::<_, _, NoTrace>( - checked_tx, - header, - coinbase_contract_id, - gas_price, - storage_tx, - memory, - )?; - (reverted, state, tx, receipts, Vec::new()) - } - }; + let (reverted, state, tx, receipts) = self + .attempt_tx_execution_with_vm::<_, _>( + checked_tx, + header, + coinbase_contract_id, + gas_price, + storage_tx, + memory, + )?; self.spend_input_utxos(tx.inputs(), storage_tx, reverted, execution_data)?; @@ -1323,7 +1397,6 @@ where &tx, execution_data, receipts, - trace_frames, gas_price, reverted, state, @@ -1396,7 +1469,6 @@ where result: TransactionExecutionResult::Success { result: None, receipts: vec![], - execution_trace: Vec::new(), total_gas: 0, total_fee: 0, }, @@ -1504,7 +1576,6 @@ where tx: &Tx, execution_data: &mut ExecutionData, receipts: Vec, - execution_trace: Vec, gas_price: Word, reverted: bool, state: ProgramState, @@ -1539,7 +1610,6 @@ where TransactionExecutionResult::Failed { result: Some(state), receipts, - execution_trace, total_gas: used_gas, total_fee: tx_fee, } @@ -1548,7 +1618,6 @@ where TransactionExecutionResult::Success { result: Some(state), receipts, - execution_trace, total_gas: used_gas, total_fee: tx_fee, } @@ -1639,7 +1708,7 @@ where } #[allow(clippy::type_complexity)] - fn attempt_tx_execution_with_vm( + fn attempt_tx_execution_with_vm( &self, checked_tx: Checked, header: &PartialBlockHeader, @@ -1647,12 +1716,11 @@ where gas_price: Word, storage_tx: &mut TxStorageTransaction, memory: &mut MemoryInstance, - ) -> ExecutorResult<(bool, ProgramState, Tx, Vec, Trace)> + ) -> ExecutorResult<(bool, ProgramState, Tx, Vec)> where Tx: ExecutableTransaction + Cacheable, ::Metadata: CheckedMetadataTrait + Send + Sync, T: KeyValueInspect, - Trace: ExecutionTraceHooks + Clone + Default, { let tx_id = checked_tx.id(); @@ -1676,9 +1744,9 @@ where .iter() .map(|input| input.predicate_gas_used()) .collect(); - let ready_tx = checked_tx.into_ready(gas_price, gas_costs, fee_params, None)?; // TODO: block_height argument? + let ready_tx = checked_tx.into_ready(gas_price, gas_costs, fee_params)?; - let mut vm = Interpreter::<_, _, _, NotSupportedEcal, Trace>::with_storage( + let mut vm = Interpreter::<_, _, _>::with_storage( memory, vm_db, InterpreterParams::new(gas_price, &self.consensus_params), @@ -1693,7 +1761,6 @@ where .into(); let reverted = vm_result.should_revert(); - let trace = vm.trace().clone(); let (state, mut tx, receipts): (_, Tx, _) = vm_result.into_inner(); #[cfg(debug_assertions)] { @@ -1715,7 +1782,7 @@ where } self.update_tx_outputs(storage_tx, tx_id, &mut tx)?; - Ok((reverted, state, tx, receipts.to_vec(), trace)) + Ok((reverted, state, tx, receipts.to_vec())) } fn verify_inputs_exist_and_values_match( @@ -2020,9 +2087,9 @@ where } /// Log a VM backtrace if configured to do so - fn log_backtrace( + fn log_backtrace( &self, - vm: &Interpreter, Tx, Ecal, Trace>, + vm: &Interpreter, Tx, Ecal>, receipts: &[Receipt], ) where M: Memory, diff --git a/crates/services/executor/src/lib.rs b/crates/services/executor/src/lib.rs index 75fa2332973..d28e5dbb612 100644 --- a/crates/services/executor/src/lib.rs +++ b/crates/services/executor/src/lib.rs @@ -10,7 +10,6 @@ extern crate alloc; pub mod executor; pub mod ports; pub mod refs; -pub mod trace; #[cfg(test)] fuel_core_trace::enable_tracing!(); diff --git a/crates/services/executor/src/trace.rs b/crates/services/executor/src/trace.rs deleted file mode 100644 index 7150a4b54d3..00000000000 --- a/crates/services/executor/src/trace.rs +++ /dev/null @@ -1,181 +0,0 @@ -use alloc::vec::Vec; -use fuel_core_types::{ - fuel_vm::{ - consts::VM_REGISTER_COUNT, - interpreter::{ - trace::ExecutionTraceHooks, - Memory, - MemoryInstance, - }, - }, - services::executor::TraceFrame, -}; - -/// Used to capture an execution trace for every instruction. -#[derive(Debug, Clone, Default)] -pub struct TraceOnInstruction { - /// Append-only set of frames - pub frames: Vec, - /// Memory at the time of the previous snapshot - acc_memory: AccVec, -} - -impl ExecutionTraceHooks for TraceOnInstruction { - fn after_instruction( - vm: &mut fuel_core_types::fuel_vm::Interpreter, - ) where - M: Memory, - { - let memory_diff = vm.trace().acc_memory.diff(vm.memory().as_ref()); - for patch in memory_diff.parts.iter() { - vm.trace_mut().acc_memory.update(patch.clone()); - } - - let mut registers = [0; VM_REGISTER_COUNT]; - registers.copy_from_slice(vm.registers()); - - let receipt_count = vm.receipts().len(); - - vm.trace_mut().frames.push(TraceFrame { - memory_diff: memory_diff - .parts - .into_iter() - .map(|p| (p.start, p.data)) - .collect(), - registers, - receipt_count, - }) - } -} - -/// Used to capture an execution trace for after each receipt. -#[derive(Debug, Clone, Default)] -pub struct TraceOnReceipt { - /// Append-only set of frames - pub frames: Vec, - /// Memory at the time of the previous snapshot - acc_memory: AccVec, -} - -impl ExecutionTraceHooks for TraceOnReceipt { - fn after_instruction( - vm: &mut fuel_core_types::fuel_vm::Interpreter, - ) where - M: Memory, - { - if vm - .trace() - .frames - .last() - .map(|s| s.receipt_count) - .unwrap_or(0) - < vm.receipts().len() - { - let memory_diff = vm.trace().acc_memory.diff(vm.memory().as_ref()); - for patch in memory_diff.parts.iter() { - vm.trace_mut().acc_memory.update(patch.clone()); - } - - let mut registers = [0; VM_REGISTER_COUNT]; - registers.copy_from_slice(vm.registers()); - - let receipt_count = vm.receipts().len(); - - vm.trace_mut().frames.push(TraceFrame { - memory_diff: memory_diff - .parts - .into_iter() - .map(|p| (p.start, p.data)) - .collect(), - registers, - receipt_count, - }) - } - } -} - -#[derive(Debug, Clone, Default)] -struct AccVec { - parts: Vec, -} - -#[derive(Debug, Clone)] -struct AccVecPart { - start: usize, - data: Vec, -} -impl AccVecPart { - #[allow(clippy::arithmetic_side_effects)] // VM memory is always within bounds - fn end(&self) -> usize { - self.start + self.data.len() - } -} - -impl AccVec { - #[allow(clippy::arithmetic_side_effects)] // All operations stay within array bounds - pub fn update(&mut self, mut new: AccVecPart) { - let new_end = new.end(); - let start = self.parts.binary_search_by_key(&new.start, |p| p.start); - - // Figure out the number of overlapping with the new part - let mut end = match start { - Ok(start) => start, - Err(start) => start, - }; - while end < self.parts.len() && self.parts[end].start < new.end() { - end += 1; - } - - // Actually insert the new part - match start { - Ok(start) => { - // Found a part that starts at exact same address. Figure out how many pieces we need to merge. - if start == end { - // We only have one item. - if self.parts[start].data.len() <= new.data.len() { - // Replace a prefix - self.parts[start].data[..new.data.len()] - .copy_from_slice(&new.data); - } else { - // Replace and extend - self.parts[start] = new; - } - return; - } - - // Multiple items to merge - self.parts[start].data = new.data; - - // Remove the now-unnecessary parts, but keep the last one in case we need to keep some of it. - if let Some(last) = self.parts.drain(start + 1..end).last() { - // How many bytes of the last item we need to keep? - let keep_last = last.end() - new_end; - self.parts[start] - .data - .extend_from_slice(&last.data[keep_last..]); - } - } - Err(start) => { - // No exact match for start address found. - - if start == self.parts.len() { - // This is the last item, so we can just append it. - self.parts.push(new); - return; - } - - // Remove the now-unnecessary parts, but keep the last one in case we need to keep some of it. - if let Some(last) = self.parts.drain(start..end).last() { - let keep_last = last.end() - new_end; - new.data.extend_from_slice(&last.data[keep_last..]); - } - - self.parts.insert(start, new); - } - } - } - - pub fn diff(&self, _new: &MemoryInstance) -> Self { - todo!(); - } -} diff --git a/crates/services/producer/src/block_producer.rs b/crates/services/producer/src/block_producer.rs index f82a05b96b0..d013dc4d405 100644 --- a/crates/services/producer/src/block_producer.rs +++ b/crates/services/producer/src/block_producer.rs @@ -43,7 +43,7 @@ use fuel_core_types::{ services::{ block_producer::Components, executor::{ - TraceTrigger, + StorageReadReplayEvent, TransactionExecutionStatus, UncommittedResult, }, @@ -384,16 +384,15 @@ impl where ViewProvider: HistoricalView + 'static, ViewProvider::LatestView: BlockProducerDatabase, - Executor: ports::BlockExecutionTracer + 'static, + Executor: ports::StorageReadReplayRecorder + 'static, GasPriceProvider: GasPriceProviderConstraint, ConsensusProvider: ConsensusParametersProvider, { - /// Re-executes an old block, getting full execution traces. - pub async fn execution_trace_block( + /// Re-executes an old block, getting the storage read events. + pub async fn storage_read_replay( &self, height: BlockHeight, - trigger: TraceTrigger, - ) -> anyhow::Result> { + ) -> anyhow::Result>> { let view = self.view_provider.latest_view()?; let block = view.get_block(&height)?; @@ -404,7 +403,7 @@ where .collect::, _>>()?; let block = block.into_owned().uncompress(transactions); - Ok(self.executor.execution_trace(&block, trigger)?) + Ok(self.executor.storage_read_replay(&block)?) } } diff --git a/crates/services/producer/src/ports.rs b/crates/services/producer/src/ports.rs index 9bc3a50bded..c60cbf8435d 100644 --- a/crates/services/producer/src/ports.rs +++ b/crates/services/producer/src/ports.rs @@ -24,7 +24,7 @@ use fuel_core_types::{ block_producer::Components, executor::{ Result as ExecutorResult, - TraceTrigger, + StorageReadReplayEvent, TransactionExecutionStatus, UncommittedResult, }, @@ -110,10 +110,9 @@ pub trait DryRunner: Send + Sync { ) -> ExecutorResult>; } -pub trait BlockExecutionTracer: Send + Sync { - fn execution_trace( +pub trait StorageReadReplayRecorder: Send + Sync { + fn storage_read_replay( &self, block: &Block, - trigger: TraceTrigger, - ) -> ExecutorResult>; + ) -> ExecutorResult>>; } diff --git a/crates/services/upgradable-executor/Cargo.toml b/crates/services/upgradable-executor/Cargo.toml index 16eeb0fc391..c93cf1e3d42 100644 --- a/crates/services/upgradable-executor/Cargo.toml +++ b/crates/services/upgradable-executor/Cargo.toml @@ -11,7 +11,8 @@ description = "Fuel Block Upgradable Executor" build = "build.rs" [dependencies] -anyhow = { workspace = true, optional = true } +anyhow = { workspace = true } +# anyhow = { workspace = true, optional = true } derive_more = { workspace = true, optional = true } fuel-core-executor = { workspace = true } fuel-core-storage = { workspace = true, features = ["std"] } @@ -47,7 +48,7 @@ smt = [ "fuel-core-wasm-executor?/smt", ] wasm-executor = [ - "dep:anyhow", + # "dep:anyhow", "dep:derive_more", "dep:parking_lot", "dep:postcard", diff --git a/crates/services/upgradable-executor/src/config.rs b/crates/services/upgradable-executor/src/config.rs index 3ba9de4f7ea..7367cf318e3 100644 --- a/crates/services/upgradable-executor/src/config.rs +++ b/crates/services/upgradable-executor/src/config.rs @@ -20,7 +20,6 @@ impl From<&Config> for ExecutionOptions { Self { extra_tx_checks: value.utxo_validation_default, backtrace: value.backtrace, - execution_trace: None, } } } diff --git a/crates/services/upgradable-executor/src/executor.rs b/crates/services/upgradable-executor/src/executor.rs index 8e8696b7a4b..88312abe283 100644 --- a/crates/services/upgradable-executor/src/executor.rs +++ b/crates/services/upgradable-executor/src/executor.rs @@ -1,6 +1,10 @@ -use crate::config::Config; #[cfg(feature = "wasm-executor")] use crate::error::UpgradableError; +use crate::{ + config::Config, + relayer_recorder::RelayerRecorder, + storage_access_recorder::StorageAccessRecorder, +}; use fuel_core_executor::{ executor::{ @@ -41,7 +45,7 @@ use fuel_core_types::{ Error as ExecutorError, ExecutionResult, Result as ExecutorResult, - TraceTrigger, + StorageReadReplayEvent, TransactionExecutionStatus, ValidationResult, }, @@ -342,7 +346,6 @@ where let options = ExecutionOptions { extra_tx_checks: utxo_validation, backtrace: self.config.backtrace, - execution_trace: None, }; let component = Components { @@ -376,15 +379,50 @@ where self.validate_inner(block, options) } - pub fn execution_traces( + pub fn storage_read_replay( &self, block: &Block, - trigger: TraceTrigger, - ) -> ExecutorResult> { - let mut options: ExecutionOptions = self.config.as_ref().into(); - options.execution_trace = Some(trigger); - self.validate_inner(block, options) - .map(|v| v.into_result().tx_status) + ) -> ExecutorResult>> { + // HERE! + + let block_version = block.header().state_transition_bytecode_version; + let native_executor_version = self.native_executor_version(); + if block_version != native_executor_version { + todo!("Handle this. Wasm?"); // TODO + } + + let previous_block_height = block.header().height().pred(); + let relayer = self.relayer_view_provider.latest_view()?; + let relayer = RelayerRecorder::new(relayer); + let relayer_rec = relayer.record.clone(); + + let mut accesses_per_tx = Vec::new(); + + if let Some(previous_block_height) = previous_block_height { + let database = self.storage_view_provider.view_at(&previous_block_height)?; + let database = StorageAccessRecorder::new(database); + let database_rec = database.record.clone(); + + let executor = + ExecutionInstance::new(relayer, database, self.config.as_ref().into()); + + let result = executor.record_storage_reads_for(block, &mut |index| { + println!("== tx {index} starts =="); + let changes = database_rec.take(); + if index > 0 { + // We don't care about block-level accesses, only the tx-level ones + accesses_per_tx.push(changes); + } + })?; + + dbg!(&accesses_per_tx, database_rec, relayer_rec); + + result + } else { + todo!(); + }; + + Ok(accesses_per_tx) } #[cfg(feature = "wasm-executor")] diff --git a/crates/services/upgradable-executor/src/lib.rs b/crates/services/upgradable-executor/src/lib.rs index baade6d4fab..4cfb9de5dc3 100644 --- a/crates/services/upgradable-executor/src/lib.rs +++ b/crates/services/upgradable-executor/src/lib.rs @@ -7,6 +7,9 @@ pub mod config; pub mod error; pub mod executor; +mod relayer_recorder; +mod storage_access_recorder; + #[cfg(feature = "wasm-executor")] pub mod instance; diff --git a/crates/services/upgradable-executor/src/relayer_recorder.rs b/crates/services/upgradable-executor/src/relayer_recorder.rs new file mode 100644 index 00000000000..f4ce2e7be3f --- /dev/null +++ b/crates/services/upgradable-executor/src/relayer_recorder.rs @@ -0,0 +1,50 @@ +use fuel_core_executor::ports::RelayerPort; +use fuel_core_types::{ + blockchain::primitives::DaBlockHeight, + services::relayer::Event, +}; +use std::{ + cell::RefCell, + collections::BTreeMap, + sync::Arc, +}; + +#[derive(Debug, Clone, Default)] +pub struct Relayer(RefCell>>); + +impl Relayer { + pub fn add_event(&self, da_block_height: DaBlockHeight, events: Vec) { + self.0.borrow_mut().insert(da_block_height, events); + } +} + +#[derive(Debug, Clone)] +pub struct RelayerRecorder { + storage: S, + pub record: Arc>, +} + +impl RelayerRecorder { + pub fn new(storage: S) -> Self { + Self { + storage, + record: Default::default(), + } + } +} + +impl RelayerPort for RelayerRecorder +where + S: RelayerPort, +{ + fn enabled(&self) -> bool { + self.storage.enabled() + } + + fn get_events(&self, da_height: &DaBlockHeight) -> anyhow::Result> { + println!("events for {:?}", da_height); // TODO: remove this + let events = self.storage.get_events(da_height)?; + self.record.borrow().add_event(*da_height, events.clone()); + Ok(events) + } +} diff --git a/crates/services/upgradable-executor/src/storage_access_recorder.rs b/crates/services/upgradable-executor/src/storage_access_recorder.rs new file mode 100644 index 00000000000..23704efd285 --- /dev/null +++ b/crates/services/upgradable-executor/src/storage_access_recorder.rs @@ -0,0 +1,51 @@ +use fuel_core_storage::{ + kv_store::{ + KeyValueInspect, + StorageColumn, + Value, + }, + Result as StorageResult, +}; +use fuel_core_types::services::executor::StorageReadReplayEvent; +use std::{ + cell::RefCell, + sync::Arc, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorageAccessRecorder +where + S: KeyValueInspect, +{ + pub storage: S, + pub record: Arc>>, +} + +impl StorageAccessRecorder +where + S: KeyValueInspect, +{ + pub fn new(storage: S) -> Self { + Self { + storage, + record: Default::default(), + } + } +} + +impl KeyValueInspect for StorageAccessRecorder +where + S: KeyValueInspect, +{ + type Column = S::Column; + + fn get(&self, key: &[u8], column: Self::Column) -> StorageResult> { + let value = self.storage.get(key, column)?; + self.record.borrow_mut().push(StorageReadReplayEvent { + column: column.name(), + key: key.to_vec(), + value: value.as_ref().map(|v| v.to_vec()), + }); + Ok(value) + } +} diff --git a/crates/storage/src/kv_store.rs b/crates/storage/src/kv_store.rs index 214b46fe51b..3e7c1359114 100644 --- a/crates/storage/src/kv_store.rs +++ b/crates/storage/src/kv_store.rs @@ -67,23 +67,17 @@ pub trait KeyValueInspect { &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { self.get(key, column)? .map(|value| { let read = value.len(); - if offset.saturating_add(read) != buf.len() { + if read != buf.len() { return Err(StorageError::Other(anyhow::anyhow!( - "Buffer size is not equal to the value size after offset" + "Buffer size is not equal to the value size" ))); } - if offset >= buf.len() { - return Err(StorageError::Other(anyhow::anyhow!( - "Offset too large for the buffer" - ))); - } - buf.copy_from_slice(&value.as_ref()[offset..]); + buf.copy_from_slice(value.as_ref()); Ok(read) }) .transpose() @@ -117,10 +111,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.deref().read(key, column, offset, buf) + self.deref().read(key, column, buf) } } diff --git a/crates/storage/src/structured_storage.rs b/crates/storage/src/structured_storage.rs index 4e1239c5982..122792cbf8f 100644 --- a/crates/storage/src/structured_storage.rs +++ b/crates/storage/src/structured_storage.rs @@ -147,10 +147,9 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { - self.inner.read(key, column, offset, buf) + self.inner.read(key, column, buf) } } @@ -359,7 +358,6 @@ where fn read( &self, key: &::Key, - offset: usize, buf: &mut [u8], ) -> Result, Self::Error> { let key_encoder = @@ -367,12 +365,8 @@ where key, ); let key_bytes = key_encoder.as_bytes(); - self.inner.read( - key_bytes.as_ref(), - ::column(), - offset, - buf, - ) + self.inner + .read(key_bytes.as_ref(), ::column(), buf) } fn read_alloc( diff --git a/crates/storage/src/transactional.rs b/crates/storage/src/transactional.rs index 7740828f110..4494393ad33 100644 --- a/crates/storage/src/transactional.rs +++ b/crates/storage/src/transactional.rs @@ -392,30 +392,24 @@ where &self, key: &[u8], column: Self::Column, - offset: usize, buf: &mut [u8], ) -> StorageResult> { if let Some(operation) = self.get_from_changes(key, column) { match operation { WriteOperation::Insert(value) => { let read = value.len(); - if offset.saturating_add(read) != buf.len() { + if read != buf.len() { return Err(crate::Error::Other(anyhow::anyhow!( - "Buffer size is not equal to the value size after offset" + "Buffer size is not equal to the value size" ))); } - if offset >= buf.len() { - return Err(crate::Error::Other(anyhow::anyhow!( - "Offset too large for the buffer" - ))); - } - buf.copy_from_slice(&value.as_ref()[offset..]); + buf.copy_from_slice(value.as_ref()); Ok(Some(read)) } WriteOperation::Remove => Ok(None), } } else { - self.storage.read(key, column, offset, buf) + self.storage.read(key, column, buf) } } } diff --git a/crates/storage/src/vm_storage.rs b/crates/storage/src/vm_storage.rs index 99f6c360628..6188c3cf85c 100644 --- a/crates/storage/src/vm_storage.rs +++ b/crates/storage/src/vm_storage.rs @@ -193,10 +193,9 @@ where fn read( &self, key: &M::Key, - offset: usize, buf: &mut [u8], ) -> Result, Self::Error> { - StorageRead::::read(&self.database, key, offset, buf) + StorageRead::::read(&self.database, key, buf) } fn read_alloc( diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index f0587443416..10e5922ca3b 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -27,7 +27,6 @@ fuel-vm-private = { workspace = true, default-features = false, features = [ rand = { workspace = true, optional = true } secrecy = "0.8" serde = { workspace = true, features = ["derive"], optional = true } -serde-big-array = { version = "0.5", default-features = false} # We force the version because 4.1.0 update leap seconds that breaks our timestamps tai64 = { version = "=4.0.0", features = ["serde"] } zeroize = "1.5" diff --git a/crates/types/src/services/executor.rs b/crates/types/src/services/executor.rs index a5168382f3a..30a4fbe7b74 100644 --- a/crates/types/src/services/executor.rs +++ b/crates/types/src/services/executor.rs @@ -26,11 +26,9 @@ use crate::{ Bytes32, ContractId, Nonce, - Word, }, fuel_vm::{ checked_transaction::CheckError, - consts::VM_REGISTER_COUNT, ProgramState, }, services::Uncommitted, @@ -199,8 +197,6 @@ pub enum TransactionExecutionResult { result: Option, /// The receipts generated by the executed transaction. receipts: Vec, - /// Full execution trace, if recorded. - execution_trace: Vec, /// The total gas used by the transaction. total_gas: u64, /// The total fee paid by the transaction. @@ -212,8 +208,6 @@ pub enum TransactionExecutionResult { result: Option, /// The receipts generated by the executed transaction. receipts: Vec, - /// Full execution trace, if recorded. - execution_trace: Vec, /// The total gas used by the transaction. total_gas: u64, /// The total fee paid by the transaction. @@ -262,6 +256,18 @@ impl TransactionExecutionResult { } } +/// When storage in column:key was read, it contained this value. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct StorageReadReplayEvent { + /// Column in the storage, identified by name. + pub column: String, + /// Key in the column. + pub key: Vec, + /// Value at the column:key pair. None if the key was not found. + pub value: Option>, +} + #[allow(missing_docs)] #[derive(Debug, Clone, PartialEq, derive_more::Display, derive_more::From)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -405,25 +411,3 @@ impl From for TransactionValidityError { Self::Validation(CheckError::Validity(e)) } } - -/// Snapshot of the execution state, with some delta compression applied -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TraceFrame { - /// Registers at this point - #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))] - pub registers: [Word; VM_REGISTER_COUNT], - /// Memory delta from the previous snapshot - pub memory_diff: Vec<(usize, Vec)>, - /// How many of the receipts have been added by now - pub receipt_count: usize, -} - -/// When to record a new snapshot -#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] -pub enum TraceTrigger { - /// Capture state after an instruction adds a new receipt - OnReceipt, - /// Capture state after each instruction - OnInstruction, -} diff --git a/crates/types/src/services/txpool.rs b/crates/types/src/services/txpool.rs index dd0ee5c835f..c684a78b092 100644 --- a/crates/types/src/services/txpool.rs +++ b/crates/types/src/services/txpool.rs @@ -377,7 +377,6 @@ pub fn from_executor_to_status( receipts, total_gas, total_fee, - execution_trace: _, // Discarded, txpool doesn't use these } => TransactionStatus::Success { block_height, time, @@ -391,7 +390,6 @@ pub fn from_executor_to_status( receipts, total_gas, total_fee, - execution_trace: _, // Discarded, txpool doesn't use these } => TransactionStatus::Failed { block_height, time,