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

Dry run in the past #2661

Merged
merged 14 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [2630](https://github.com/FuelLabs/fuel-core/pull/2630): Removed some noisy `tracing::info!` logs
- [2643](https://github.com/FuelLabs/fuel-core/pull/2643): Before this fix when tip is zero, transactions that use 30M have the same priority as transactions with 1M gas. Now they are correctly ordered.

### Breaking
- [2661](https://github.com/FuelLabs/fuel-core/pull/2661): Dry run now supports running in past blocks. `dry_run_opt` method now takes block number as the last argument. To retain old behavior, simply pass in `None` for the last argument.

### Added
- [2617](https://github.com/FuelLabs/fuel-core/pull/2617): Add integration skeleton of parallel-executor.
- [2553](https://github.com/FuelLabs/fuel-core/pull/2553): Scaffold global merkle root storage crate.
Expand Down
2 changes: 1 addition & 1 deletion bin/e2e-test-client/src/tests/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ async fn _dry_runs(
let query = ctx
.alice
.client
.dry_run_opt(transactions, Some(false), None)
.dry_run_opt(transactions, Some(false), None, None)
.await;
println!(
"Received the response for the query number {i} for {}ms",
Expand Down
7 changes: 7 additions & 0 deletions bin/fuel-core/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ pub struct Command {
#[arg(long = "debug", env)]
pub debug: bool,

/// Allows execution of transactions based on past block, such as:
/// - Dry run in the past
#[arg(long = "historical-execution", env)]
pub historical_execution: bool,

/// Enable logging of backtraces from vm errors
#[arg(long = "vm-backtrace", env)]
pub vm_backtrace: bool,
Expand Down Expand Up @@ -305,6 +310,7 @@ impl Command {
continue_on_error,
vm_backtrace,
debug,
historical_execution,
utxo_validation,
native_executor_version,
#[cfg(feature = "parallel-executor")]
Expand Down Expand Up @@ -623,6 +629,7 @@ impl Command {
combined_db_config,
snapshot_reader,
debug,
historical_execution,
native_executor_version,
continue_on_error,
utxo_validation,
Expand Down
2 changes: 1 addition & 1 deletion crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ type Mutation {
"""
Execute a dry-run of multiple transactions using a fork of current state, no changes are committed.
"""
dryRun(txs: [HexString!]!, utxoValidation: Boolean, gasPrice: U64): [DryRunTransactionExecutionStatus!]!
dryRun(txs: [HexString!]!, utxoValidation: Boolean, gasPrice: U64, blockHeight: U32): [DryRunTransactionExecutionStatus!]!
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we should consider combining the utxoValidation, gasPrice and blockHeight args into a single input struct. This would allow us to add more fields in the future without breaking the API (though currently it's still breaking if we use the new arg in the client until we resolve #2676).

If you think this sounds sensible, we could create a follow-up issue for it.

Copy link
Member Author

Choose a reason for hiding this comment

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

The extra args are already optional, isn't that already good enough? But it's good to consider the forward compatibility. I'm not sure if a struct would help in this case?

"""
Submits transaction to the `TxPool`.

Expand Down
4 changes: 3 additions & 1 deletion crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ impl FuelClient {
&self,
txs: &[Transaction],
) -> io::Result<Vec<TransactionExecutionStatus>> {
self.dry_run_opt(txs, None, None).await
self.dry_run_opt(txs, None, None, None).await
}

/// Dry run with options to override the node behavior
Expand All @@ -490,6 +490,7 @@ impl FuelClient {
// Disable utxo input checks (exists, unspent, and valid signature)
utxo_validation: Option<bool>,
gas_price: Option<u64>,
at_height: Option<BlockHeight>,
) -> io::Result<Vec<TransactionExecutionStatus>> {
let txs = txs
.iter()
Expand All @@ -500,6 +501,7 @@ impl FuelClient {
txs,
utxo_validation,
gas_price: gas_price.map(|gp| gp.into()),
block_height: at_height.map(|bh| bh.into()),
});
let tx_statuses = self.query(query).await.map(|r| r.dry_run)?;
tx_statuses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
source: crates/client/src/client/schema/tx.rs
expression: query.query
---
mutation DryRun($txs: [HexString!]!, $utxoValidation: Boolean, $gasPrice: U64) {
dryRun(txs: $txs, utxoValidation: $utxoValidation, gasPrice: $gasPrice) {
mutation DryRun($txs: [HexString!]!, $utxoValidation: Boolean, $gasPrice: U64, $blockHeight: U32) {
dryRun(txs: $txs, utxoValidation: $utxoValidation, gasPrice: $gasPrice, blockHeight: $blockHeight) {
id
status {
__typename
Expand Down
4 changes: 3 additions & 1 deletion crates/client/src/client/schema/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ pub struct DryRunArg {
pub txs: Vec<HexString>,
pub utxo_validation: Option<bool>,
pub gas_price: Option<U64>,
pub block_height: Option<U32>,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
Expand All @@ -442,7 +443,7 @@ pub struct DryRunArg {
variables = "DryRunArg"
)]
pub struct DryRun {
#[arguments(txs: $txs, utxoValidation: $utxo_validation, gasPrice: $gas_price)]
#[arguments(txs: $txs, utxoValidation: $utxo_validation, gasPrice: $gas_price, blockHeight: $block_height)]
pub dry_run: Vec<DryRunTransactionExecutionStatus>,
}

Expand Down Expand Up @@ -560,6 +561,7 @@ pub mod tests {
txs: vec![HexString(Bytes(tx.to_bytes()))],
utxo_validation: Some(true),
gas_price: Some(123u64.into()),
block_height: Some(456u32.into()),
});
insta::assert_snapshot!(query.query)
}
Expand Down
1 change: 1 addition & 0 deletions crates/fuel-core/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ mod tests {
backtrace: config.backtrace,
utxo_validation_default: config.utxo_validation_default,
native_executor_version: None,
allow_historical_execution: true,
};

let database = add_consensus_parameters(database, &config.consensus_parameters);
Expand Down
19 changes: 17 additions & 2 deletions crates/fuel-core/src/schema/tx.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use super::scalars::U64;
use super::scalars::{
U32,
U64,
};
use crate::{
fuel_core_graphql_api::{
api_service::{
Expand All @@ -7,6 +10,7 @@ use crate::{
TxPool,
},
query_costs,
Config as GraphQLConfig,
IntoApiResult,
},
graphql_api::{
Expand Down Expand Up @@ -293,13 +297,24 @@ impl TxMutation {
// for read-only calls.
utxo_validation: Option<bool>,
gas_price: Option<U64>,
// This can be used to run the dry-run on top of a past block.
// Requires `--historical-execution` flag to be enabled.
block_height: Option<U32>,
) -> async_graphql::Result<Vec<DryRunTransactionExecutionStatus>> {
let config = ctx.data_unchecked::<GraphQLConfig>().clone();
let block_producer = ctx.data_unchecked::<BlockProducer>();
let consensus_params = ctx
.data_unchecked::<ConsensusProvider>()
.latest_consensus_params();
let block_gas_limit = consensus_params.block_gas_limit();

if block_height.is_some() && !config.debug {
return Err(anyhow::anyhow!(
"The `blockHeight` parameter requires the `--debug` option"
)
.into());
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

It still uses --debug and .debug.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in 2245cc1

let mut transactions = txs
.iter()
.map(|tx| FuelTx::from_bytes(&tx.0))
Expand All @@ -317,7 +332,7 @@ impl TxMutation {
let tx_statuses = block_producer
.dry_run_txs(
transactions,
None, // TODO(#1749): Pass parameter from API
block_height.map(|x| x.into()),
None, // TODO(#1749): Pass parameter from API
utxo_validation,
gas_price.map(|x| x.into()),
Expand Down
6 changes: 3 additions & 3 deletions crates/fuel-core/src/service/adapters/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ use fuel_core_types::{
},
primitives::DaBlockHeight,
},
fuel_tx,
fuel_tx::{
ConsensusParameters,
Transaction,
Expand Down Expand Up @@ -117,10 +116,11 @@ impl fuel_core_producer::ports::BlockProducer<Vec<Transaction>> for ExecutorAdap
impl fuel_core_producer::ports::DryRunner for ExecutorAdapter {
fn dry_run(
&self,
block: Components<Vec<fuel_tx::Transaction>>,
block: Components<Vec<Transaction>>,
utxo_validation: Option<bool>,
at_height: Option<BlockHeight>,
) -> ExecutorResult<Vec<TransactionExecutionStatus>> {
self.executor.dry_run(block, utxo_validation)
self.executor.dry_run(block, utxo_validation, at_height)
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/fuel-core/src/service/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pub struct Config {
/// - Enables debugger endpoint.
/// - Allows setting `utxo_validation` to `false`.
pub debug: bool,
/// When `true`:
/// - Enables dry run in the past.
pub historical_execution: bool,
// default to false until downstream consumers stabilize
pub utxo_validation: bool,
pub native_executor_version: Option<StateTransitionBytecodeVersion>,
Expand Down Expand Up @@ -158,6 +161,7 @@ impl Config {
combined_db_config,
continue_on_error: false,
debug: true,
historical_execution: true,
utxo_validation,
native_executor_version: Some(native_executor_version),
#[cfg(feature = "parallel-executor")]
Expand Down
5 changes: 5 additions & 0 deletions crates/fuel-core/src/service/sub_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::{
P2PAdapter,
},
genesis::create_genesis_block,
DbType,
};
#[cfg(feature = "relayer")]
use crate::relayer::Config as RelayerConfig;
Expand Down Expand Up @@ -102,10 +103,14 @@ pub fn init_sub_services(

let last_height = *last_block_header.height();

let allow_historical_execution = config.combined_db_config.database_type
== DbType::RocksDb
&& config.historical_execution;
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 allow it on config level for the whole fuel-core, including the GraphQL and other services.

Copy link
Member Author

Choose a reason for hiding this comment

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

let upgradable_executor_config = fuel_core_upgradable_executor::config::Config {
backtrace: config.vm.backtrace,
utxo_validation_default: config.utxo_validation,
native_executor_version: config.native_executor_version,
allow_historical_execution,
};
let executor = ExecutorAdapter::new(
database.on_chain().clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/services/producer/src/block_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ where
// use the blocking threadpool for dry_run to avoid clogging up the main async runtime
let tx_statuses = tokio_rayon::spawn_fifo(
move || -> anyhow::Result<Vec<TransactionExecutionStatus>> {
Ok(executor.dry_run(component, utxo_validation)?)
Ok(executor.dry_run(component, utxo_validation, height)?)
},
)
.await?;
Expand Down
1 change: 1 addition & 0 deletions crates/services/producer/src/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl DryRunner for MockExecutorWithCapture {
&self,
block: Components<Vec<Transaction>>,
_utxo_validation: Option<bool>,
_height: Option<BlockHeight>,
) -> ExecutorResult<Vec<TransactionExecutionStatus>> {
*self.captured.lock().unwrap() = Some(block);

Expand Down
3 changes: 2 additions & 1 deletion crates/services/producer/src/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ pub trait BlockProducer<TxSource>: Send + Sync {
pub trait DryRunner: Send + Sync {
/// Executes the block without committing it to the database. During execution collects the
/// receipts to return them. The `utxo_validation` field can be used to disable the validation
/// of utxos during execution.
/// of utxos during execution. The `at_height` field can be used to dry run on top of a past block.
fn dry_run(
&self,
block: Components<Vec<Transaction>>,
utxo_validation: Option<bool>,
at_height: Option<BlockHeight>,
) -> ExecutorResult<Vec<TransactionExecutionStatus>>;
}
3 changes: 3 additions & 0 deletions crates/services/upgradable-executor/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub struct Config {
/// When a block version matches the native executor version, we use
/// the native executor; otherwise, we use the WASM executor.
pub native_executor_version: Option<StateTransitionBytecodeVersion>,
/// Allow execution using blocks in the past.
/// This is rather expensive and not needed for most use cases, so it can be disabled.
pub allow_historical_execution: bool,
}

impl From<&Config> for ExecutionOptions {
Expand Down
Loading
Loading