-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Separate production from dry runs in executor & Cleanup all execution paths :) #1873
Separate production from dry runs in executor & Cleanup all execution paths :) #1873
Conversation
…from-prod-execution
Ok(()) | ||
} | ||
|
||
#[tracing::instrument(skip_all)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this redundant? We also have a skip_all
on dry_run_without_commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, plus I think maybe it is better to remove this lof from dry_run
at all
let component = { | ||
Components { | ||
header_to_produce: block.header, | ||
transactions_source: OnceTransactionsSource::new(block.transactions), | ||
coinbase_recipient, | ||
gas_price, | ||
}), | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let component = { | |
Components { | |
header_to_produce: block.header, | |
transactions_source: OnceTransactionsSource::new(block.transactions), | |
coinbase_recipient, | |
gas_price, | |
}), | |
} | |
}; | |
let component = Components { | |
header_to_produce: block.header, | |
transactions_source: OnceTransactionsSource::new(block.transactions), | |
coinbase_recipient, | |
gas_price, | |
}; |
"The block version({}) is different from the native executor version({}). \ | ||
The WASM executor will be used.", block_version, Self::VERSION | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this string is now duplicated, it's worth considering if it should be a constant - warning/error message are one of those things that often get overlooked during a change, so we run the risk of having these messages become out of sync.
I would say the same thing about the duplicated error string in the non-wasm error version of dry_run_inner
.
let block_version = block.header_to_produce.state_transition_bytecode_version; | ||
let native_executor_version = self.native_executor_version(); | ||
if block_version == native_executor_version { | ||
match &self.execution_strategy { | ||
ExecutionStrategy::Native => self.native_execute_inner(block, options), | ||
ExecutionStrategy::Native => self.native_dry_run_inner(block, options), | ||
ExecutionStrategy::Wasm { module } => { | ||
self.wasm_execute_inner(module, block, options) | ||
self.wasm_dry_run_inner(module, block, options) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although this introduces some code duplication, the benefit is that it's simple to read and understand. However, I'm curious if you explored any alternative ways to implement these functions that may be a little DRYer. It is understandably challenging given the number of permuations dry run, validation, and Native/Wasm/wasm-executor enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Been looking at this. I think the only part I'd really want to DRY up more would be
let block_version = block.header_to_produce.state_transition_bytecode_version;
let native_executor_version = self.native_executor_version();
if block_version == native_executor_version { .. }
But honestly, that would save us like 1 line, especially since validate takes a different Block
type than the other two, so to DRY all three, we'd have to get the version as a separate line.
I think it's already flat, and easy to read.
WRT to the functions doing essentially the same thing, I think they can all vary independently, so I don't think we should create an abstraction here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to find a little place to DRY up the code a bit. Added new_native_executor_instance
method.
fn process_l1_and_l2_txs<T, TxSource>( | ||
&self, | ||
mut block: PartialFuelBlock, | ||
components: &Components<TxSource>, | ||
l1_transactions: Vec<CheckedTransaction>, | ||
storage_tx: &mut T, | ||
data: &mut ExecutionData, | ||
skip_failed_txs: bool, | ||
) -> ExecutorResult<PartialFuelBlock> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be feasible to break this up into something like process_l1
and process_l2
, and simply call these two functions sequentially?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. It's worth looking at a bit more. It does seem like a smell to me as well. I included them together because produce
and dry_run
used the exact same code block, so it was a nice way to group those together. I think there probably exists a cleaner solution.
The l2
code should proabably be able to query the relayed txs from the relayer, but due to some &mut
stuff I was dissuaded from doing that in one place, and left the query before generating the block_storage_tx
. Now that everything is a bit more stable I'll give it another try.
… remove skipped txs bool and always skip
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated one of the tests to show the problem with block validation=)
} | ||
|
||
#[cfg(feature = "wasm-executor")] | ||
fn dry_run_inner<TxSource>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like duplicating the whole stack for the dry run when they are the same as production.
I think ExecutionBlockWithSource
can still remain a private type of this crate just to avoid unnecessary duplication.
The change causes new 150 unnecessary lines that do exactly the same.
If you want, you can extract the source on the upper level(like dry_run_inner
/produce_run_inner
), and then you can pass the InputType
and source as separate variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I just added a dry_run
boolean param to some of the internal functions to branch. I don't think we need a type. I kept the external functions the same, partly because we have a bunch of test functions that won't ever be used for dry_run, so I didn't want to clutter the interfaces with the extra param.
Ok(()) | ||
} | ||
|
||
#[tracing::instrument(skip_all)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, plus I think maybe it is better to remove this lof from dry_run
at all
PartialFuelBlock::new(components.header_to_produce, vec![]); | ||
let mut data = ExecutionData::new(); | ||
|
||
self.process_l1_txs( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe it makes sense to remove the processing of L1 for now. Because we don't allow increasing of the da block height during dry runs.
self.block_st_transaction | ||
.commit_changes(block_with_relayer_data_transaction.into_changes())?; | ||
|
||
if execution_kind != ExecutionKind::DryRun && !data.found_mint { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be nice to add a debug assert to verify that found_mint
is true
.
I've added a solution. The route I went will check that all the L1 transactions are included in the block before executing those txs. Is that what we want though? Does the validator care whether the txs come from L1 or L2? We want to have the producer always include them immediately, at least for now, but IDK if the validator should check that. What do you think? Instead we could just remove the L1 tx checks completely. |
|
||
pub fn validate_without_commit( | ||
self, | ||
block: Block, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to pass the Block
by reference and return the ValidationResult
without the Block
type inside. It can be follow-up=)
## Version v0.27.0 ### Added - [#1898](#1898): Enforce increasing of the `Executor::VERSION` on each release. ### Changed - [#1906](#1906): Makes `cli::snapshot::Command` members public such that clients can create and execute snapshot commands programmatically. This enables snapshot execution in external programs, such as the regenesis test suite. - [#1891](#1891): Regenesis now preserves `FuelBlockMerkleData` and `FuelBlockMerkleMetadata` in the off-chain table. These tables are checked when querying message proofs. - [#1886](#1886): Use ref to `Block` in validation code - [#1876](#1876): Updated benchmark to include the worst scenario for `CROO` opcode. Also include consensus parameters in bench output. - [#1879](#1879): Return the old behaviour for the `discovery_works` test. - [#1848](#1848): Added `version` field to the `Block` and `BlockHeader` GraphQL entities. Added corresponding `version` field to the `Block` and `BlockHeader` client types in `fuel-core-client`. - [#1873](#1873): Separate dry runs from block production in executor code, remove `ExecutionKind` and `ExecutionType`, remove `thread_block_transaction` concept, remove `PartialBlockComponent` type, refactor away `inner` functions. - [#1900](#1900): Update the root README as `fuel-core run` no longer has `--chain` as an option. It has been replaced by `--snapshot`. #### Breaking - [#1894](#1894): Use testnet configuration for local testnet. - [#1894](#1894): Removed support for helm chart. - [#1910](#1910): `fuel-vm` upgraded to `0.50.0`. More information in the [changelog](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.50.0). ## What's Changed * feat: Support block and header versions gql by @bvrooman in #1848 * Updated `croo` opcode benchmark to depend on the contract size by @xgreenx in #1876 * Return the old behaviour for the `discovery_works` test by @xgreenx in #1879 * Weekly `cargo update` by @github-actions in #1880 * Separate production from dry runs in executor & Cleanup all execution paths :) by @MitchTurner in #1873 * Use ref instead of owned `Block` in validation by @MitchTurner in #1886 * Weekly `cargo update` by @github-actions in #1893 * ci: fix typos programmatically by @sdankel in #1890 * feat: Preserve message proofs post-regenesis by @bvrooman in #1891 * chore: update README fuel-core run options by @K1-R1 in #1900 * Weekly `cargo update` by @github-actions in #1903 * chore: Make snapshot command members pub accessible by @bvrooman in #1906 * Use testnet configuration for local testnet by @xgreenx in #1894 * Enforce increasing of the `Executor::VERSION` on each release by @xgreenx in #1898 * Bumped the version of the `fuel-vm` to `0.50.0` by @xgreenx in #1910 ## New Contributors * @K1-R1 made their first contribution in #1900 **Full Changelog**: v0.26.0...v0.27.0
Closes: #1751
Production
fromDryRun
and removeExecutionKind
andExecutionType
.thread_block_transaction
concept because YAGNIPartialBlockComponent
typeinner
functionsblock_storage_tx
fromBlockExecutor
. This was for a couple of reasons, but the big one is that all the&mut self
s of theBlockExecutor
can only be handed out one at a time and that was making the code more complicated. We could have just deconstructedself
, but then we wouldn't have access to any methods. This was a clean way to separate them, and it makes more sense logically IMO.ExecutorInstance
still keeps them together, so maybe there is an argument so separate them higher, but this is a good improvement in the mean time.Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]