Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execution tracing: GraphQL query to get storage inputs for past blocks #2491

Merged
merged 65 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
676a08b
Initial support for historical execution tracing
Dentosal Dec 11, 2024
1e018f1
Update schema.sdl
Dentosal Dec 11, 2024
5f6c079
Fix typo exection => execution
Dentosal Dec 11, 2024
7c8548f
Make execution trace endpoint to work on block level
Dentosal Dec 12, 2024
6ebe050
Fix cli arguments
Dentosal Dec 12, 2024
cd0704a
Various fixes and work towards Rust client support
Dentosal Dec 12, 2024
63a0016
Merge branch 'master' into dento/execution-trace
Dentosal Dec 12, 2024
f76c96f
WIP
Dentosal Dec 18, 2024
3ba2bc7
Change from tracing to storage read replay recording
Dentosal Dec 19, 2024
88a2730
Merge branch 'master' into dento/execution-trace
Dentosal Dec 30, 2024
f558296
Merge branch 'master' into dento/execution-trace
Dentosal Jan 6, 2025
5abde1c
Add wasm executor support
Dentosal Jan 15, 2025
6827e0d
Move behind --debug flag
Dentosal Jan 23, 2025
acb9bab
Adjust query cost
Dentosal Jan 23, 2025
bb6c9dd
Add debug flag decription
Dentosal Jan 23, 2025
75ff219
Approve snapshot changes
Dentosal Jan 23, 2025
73cb7a6
Add a test case
Dentosal Jan 23, 2025
7d76dc3
Merge branch 'master' into dento/execution-trace
Dentosal Jan 23, 2025
341544f
Add changelog entry
Dentosal Jan 23, 2025
84140f4
fmt
Dentosal Jan 23, 2025
3587605
Use rng correctly to avoid repeat txids
Dentosal Jan 23, 2025
2a52ec2
restore #[deny(warnings)]
Dentosal Jan 23, 2025
2c2c788
fmt
Dentosal Jan 23, 2025
83e2919
clippy
Dentosal Jan 24, 2025
b0c40fb
Merge branch 'master' into dento/execution-trace
Dentosal Jan 24, 2025
de92215
more clippy
Dentosal Jan 24, 2025
31fd109
fix typo
Dentosal Jan 24, 2025
a90b559
Add TODO comments for refactoring
Dentosal Jan 24, 2025
017c03e
more clippy
Dentosal Jan 24, 2025
f740d28
Merge branch 'master' into dento/execution-trace
Dentosal Jan 27, 2025
fce82ea
Address PR comments
Dentosal Jan 29, 2025
a4a6bbf
Merge branch 'master' into dento/execution-trace
Dentosal Jan 29, 2025
4aa858e
Merge branch 'master' into dento/execution-trace
Dentosal Feb 7, 2025
fc097bf
Address PR feedback
Dentosal Feb 7, 2025
a1fd558
Merge branch 'master' into dento/execution-trace
Dentosal Feb 10, 2025
294ea09
Fix https://github.com/FuelLabs/fuel-core/pull/2661#discussion_r19489…
Dentosal Feb 10, 2025
9eeee84
Fix https://github.com/FuelLabs/fuel-core/pull/2661#discussion_r19488…
Dentosal Feb 10, 2025
8f5c527
Move from --debug to --historical-execution
Dentosal Feb 10, 2025
3e67f50
Migrate to use pre-existing counter contract test code
Dentosal Feb 10, 2025
54bec6b
Update insta snapshots
Dentosal Feb 10, 2025
3f33473
Update CHANGELOG.md
Dentosal Feb 11, 2025
d02a5dc
Update query cost
Dentosal Feb 11, 2025
71a6a56
Address PR feedback
Dentosal Feb 11, 2025
3154797
Merge branch 'master' into dento/execution-trace
Dentosal Feb 11, 2025
338ffc1
Fix tests
Dentosal Feb 11, 2025
364c7c7
Merge branch 'master' into dento/execution-trace
Dentosal Feb 11, 2025
65a5957
Fix broken merge
Dentosal Feb 11, 2025
8469824
Update crates/client/assets/schema.sdl
Dentosal Feb 12, 2025
a260194
Update CHANGELOG.md
Dentosal Feb 12, 2025
0a1b16a
Merge branch 'master' into dento/execution-trace
Dentosal Feb 12, 2025
aaaa57b
Run replay in spawn_fifo to avoid clogging main runtime
Dentosal Feb 12, 2025
1871919
Use u32 column id instead of column name string
Dentosal Feb 12, 2025
a23c2bb
Update rest of the column fields to U32
Dentosal Feb 12, 2025
684e8cc
Change the test to use column number
Dentosal Feb 12, 2025
233d29a
fmt
Dentosal Feb 12, 2025
b255c46
clippy
Dentosal Feb 12, 2025
de6b170
Fix missing import in test
Dentosal Feb 12, 2025
35fa7df
Update tests/tests/storage_read_replay.rs
Dentosal Feb 13, 2025
9c78b11
Limit debug print size of replay event keys and values to
Dentosal Feb 13, 2025
b4a1d08
Address PR review comments
Dentosal Feb 13, 2025
3659ff0
Merge branch 'master' into dento/execution-trace
Dentosal Feb 13, 2025
075e960
Fix tests to use query instead of mutation
Dentosal Feb 14, 2025
582a30c
Move get_full_block to fuel-core
Dentosal Feb 14, 2025
c080532
Fix tests
Dentosal Feb 14, 2025
8ea800b
Merge branch 'master' into dento/execution-trace
Dentosal Feb 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added
- [2150](https://github.com/FuelLabs/fuel-core/pull/2150): Upgraded `libp2p` to `0.54.1` and introduced `ConnectionLimiter` to limit pending incoming/outgoing connections.
- [2491](https://github.com/FuelLabs/fuel-core/pull/2491): Storage read replays of historical blocks for execution tracing. Only available behind `--historical-execution` flag.
- [2666](https://github.com/FuelLabs/fuel-core/pull/2666): Added two new CLI arguments to control the GraphQL queries consistency: `--graphql-block-height-tolerance` (default: `10`) and `--graphql-block-height-min-timeout` (default: `30s`). If a request requires a specific block height and the node is slightly behind, it will wait instead of failing.

### Fixed
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ postcard = "1.0"
tracing-attributes = "0.1"
tracing-subscriber = "0.3"
serde = "1.0"
serde-big-array = { version = "0.5", default-features = false }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
serde_with = { version = "3.4", default-features = false }
strum = { version = "0.25" }
Expand Down
1 change: 1 addition & 0 deletions bin/fuel-core/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ impl Command {
get_peers: graphql.costs.get_peers,
estimate_predicates: graphql.costs.estimate_predicates,
dry_run: graphql.costs.dry_run,
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,
Expand Down
8 changes: 8 additions & 0 deletions bin/fuel-core/src/cli/run/graphql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ pub struct QueryCosts {
)]
pub dry_run: usize,

/// Query costs for generating execution trace for a block.
#[clap(
long = "query-cost-storage-read-replay",
default_value = DEFAULT_QUERY_COSTS.storage_read_replay.to_string(),
env
)]
pub storage_read_replay: usize,

/// Query costs for submitting a transaction.
#[clap(
long = "query-cost-submit",
Expand Down
10 changes: 10 additions & 0 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,10 @@ type Query {
"""
allReceipts: [Receipt!]!
"""
Get execution trace for an already-executed block.
"""
storageReadReplay(height: U32!): [StorageReadReplayEvent!]!
"""
Returns true when the GraphQL API is serving requests.
"""
health: Boolean!
Expand Down Expand Up @@ -1156,6 +1160,12 @@ type StateTransitionPurpose {
root: Bytes32!
}

type StorageReadReplayEvent {
column: U32!
key: HexString!
value: HexString
}


scalar SubId

Expand Down
27 changes: 26 additions & 1 deletion crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ use fuel_core_types::{
BlockHeight,
Nonce,
},
services::executor::TransactionExecutionStatus,
services::executor::{
StorageReadReplayEvent,
TransactionExecutionStatus,
},
};
#[cfg(feature = "subscriptions")]
use futures::{
Expand Down Expand Up @@ -98,6 +101,10 @@ use schema::{
},
da_compressed::DaCompressedBlockByHeightArgs,
gas_price::BlockHorizonArgs,
storage_read_replay::{
StorageReadReplay,
StorageReadReplayArgs,
},
tx::{
TransactionsByOwnerConnectionArgs,
TxArg,
Expand Down Expand Up @@ -652,6 +659,24 @@ impl FuelClient {
.collect()
}

/// Get storage read replay for a block
pub async fn storage_read_replay(
&self,
height: &BlockHeight,
) -> io::Result<Vec<StorageReadReplayEvent>> {
let query: Operation<StorageReadReplay, StorageReadReplayArgs> =
StorageReadReplay::build(StorageReadReplayArgs {
height: (*height).into(),
});
Ok(self
.query(query)
.await
.map(|r| r.storage_read_replay)?
.into_iter()
.map(Into::into)
.collect())
}

/// Estimate predicates for the transaction
pub async fn estimate_predicates(&self, tx: &mut Transaction) -> io::Result<()> {
let serialized_tx = tx.to_bytes();
Expand Down
1 change: 1 addition & 0 deletions crates/client/src/client/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod contract;
pub mod da_compressed;
pub mod message;
pub mod node_info;
pub mod storage_read_replay;
pub mod upgrades;

pub mod gas_price;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/client/src/client/schema/storage_read_replay.rs
expression: query.query
---
mutation StorageReadReplay($height: U32!) {
storageReadReplay(height: $height) {
column
key
value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/client/src/client/schema/storage_read_replay.rs
expression: query.query
---
query StorageReadReplay($height: U32!) {
storageReadReplay(height: $height) {
column
key
value
}
}
56 changes: 56 additions & 0 deletions crates/client/src/client/schema/storage_read_replay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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: U32,
pub key: HexString,
pub value: Option<HexString>,
}
impl From<StorageReadReplayEvent>
for fuel_core_types::services::executor::StorageReadReplayEvent
{
fn from(event: StorageReadReplayEvent) -> Self {
fuel_core_types::services::executor::StorageReadReplayEvent {
column: event.column.into(),
key: event.key.into(),
value: event.value.map(Into::into),
}
}
}

#[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 = "Query",
variables = "StorageReadReplayArgs"
)]
pub struct StorageReadReplay {
#[arguments(height: $height)]
pub storage_read_replay: Vec<StorageReadReplayEvent>,
}

#[cfg(test)]
pub mod tests {
use super::*;
use fuel_core_types::fuel_types::BlockHeight;

#[test]
fn storage_read_replay_gql_output() {
use cynic::QueryBuilder;
let query = StorageReadReplay::build(StorageReadReplayArgs {
height: BlockHeight::new(1234).into(),
});
insta::assert_snapshot!(query.query)
}
}
2 changes: 1 addition & 1 deletion crates/client/src/client/schema/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ impl TryFrom<DryRunTransactionStatus> for TransactionExecutionResult {
}
}
DryRunTransactionStatus::Unknown => {
return Err(Self::Error::UnknownVariant("DryRuynTxStatus"))
return Err(Self::Error::UnknownVariant("DryRunTxStatus"))
}
})
}
Expand Down
2 changes: 2 additions & 0 deletions crates/fuel-core/src/graphql_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct Costs {
pub get_peers: usize,
pub estimate_predicates: usize,
pub dry_run: usize,
pub storage_read_replay: usize,
pub submit: usize,
pub submit_and_await: usize,
pub status_change: usize,
Expand Down Expand Up @@ -98,6 +99,7 @@ pub const DEFAULT_QUERY_COSTS: Costs = Costs {
get_peers: 40001,
estimate_predicates: 40001,
dry_run: 12000,
storage_read_replay: 40001,
submit: 40001,
submit_and_await: 40001,
status_change: 40001,
Expand Down
10 changes: 9 additions & 1 deletion crates/fuel-core/src/graphql_api/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ use fuel_core_types::{
},
fuel_vm::interpreter::Memory,
services::{
executor::TransactionExecutionStatus,
executor::{
StorageReadReplayEvent,
TransactionExecutionStatus,
},
graphql_api::ContractBalance,
p2p::PeerInfo,
txpool::TransactionStatus,
Expand Down Expand Up @@ -256,6 +259,11 @@ pub trait BlockProducerPort: Send + Sync {
utxo_validation: Option<bool>,
gas_price: Option<u64>,
) -> anyhow::Result<Vec<TransactionExecutionStatus>>;

async fn storage_read_replay(
&self,
height: BlockHeight,
) -> anyhow::Result<Vec<StorageReadReplayEvent>>;
}

#[async_trait::async_trait]
Expand Down
26 changes: 26 additions & 0 deletions crates/fuel-core/src/schema/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ use std::{
};
use types::{
DryRunTransactionExecutionStatus,
StorageReadReplayEvent,
Transaction,
};

Expand Down Expand Up @@ -277,6 +278,31 @@ impl TxQuery {
.map(Into::into)
.collect()
}

/// Get execution trace for an already-executed block.
#[graphql(complexity = "query_costs().storage_read_replay + child_complexity")]
async fn storage_read_replay(
&self,
ctx: &Context<'_>,
height: U32,
) -> async_graphql::Result<Vec<StorageReadReplayEvent>> {
let config = ctx.data_unchecked::<GraphQLConfig>();
if !config.historical_execution {
return Err(anyhow::anyhow!(
"`--historical-execution` is required for this operation"
)
.into());
}

let block_height = height.into();
let block_producer = ctx.data_unchecked::<BlockProducer>();
Ok(block_producer
.storage_read_replay(block_height)
.await?
.into_iter()
.map(StorageReadReplayEvent::from)
.collect())
}
}

#[derive(Default)]
Expand Down
33 changes: 33 additions & 0 deletions crates/fuel-core/src/schema/tx/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,39 @@ impl DryRunTransactionExecutionStatus {
}
}

pub struct StorageReadReplayEvent {
column: U32,
key: HexString,
value: Option<HexString>,
}

impl From<fuel_core_types::services::executor::StorageReadReplayEvent>
for StorageReadReplayEvent
{
fn from(event: fuel_core_types::services::executor::StorageReadReplayEvent) -> Self {
Self {
column: event.column.into(),
key: HexString(event.key),
value: event.value.map(HexString),
}
}
}

#[Object]
impl StorageReadReplayEvent {
async fn column(&self) -> U32 {
self.column
}

async fn key(&self) -> HexString {
self.key.clone()
}

async fn value(&self) -> Option<HexString> {
self.value.clone()
}
}

#[tracing::instrument(level = "debug", skip(query, txpool), ret, err)]
pub(crate) async fn get_tx_status(
id: fuel_core_types::fuel_types::Bytes32,
Expand Down
12 changes: 11 additions & 1 deletion crates/fuel-core/src/service/adapters/graphql_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ use fuel_core_types::{
fuel_types::BlockHeight,
services::{
block_importer::SharedImportResult,
executor::TransactionExecutionStatus,
executor::{
StorageReadReplayEvent,
TransactionExecutionStatus,
},
p2p::PeerInfo,
txpool::TransactionStatus,
},
Expand Down Expand Up @@ -130,6 +133,13 @@ impl BlockProducerPort for BlockProducerAdapter {
.dry_run(transactions, height, time, utxo_validation, gas_price)
.await
}

async fn storage_read_replay(
&self,
height: BlockHeight,
) -> anyhow::Result<Vec<StorageReadReplayEvent>> {
self.block_producer.storage_read_replay(height).await
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to add a back pressure here. Or use tokio_rayon::spawn_fifo like we do for dry_run inside of the BlockProducer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Done in aaaa57b.

}
}

#[async_trait::async_trait]
Expand Down
Loading
Loading