-
Notifications
You must be signed in to change notification settings - Fork 37
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
discuss: no feature gating in trace_decoder #643
Conversation
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.
Overall I'm not really convinced that hiding away network-specific logic with this kind of tricks to not rely on feature-gating is the best approach.
pre-execution
is 1 single example, but realistically we should apply this to all network specific pieces of logic, like post-state execution, custom list of precompile addresses, withdrawals, system contracts ... Note also that we would probably need to extend the CLI to inform the network (in the future the HF too), so that zero/
crates can also get rid of feature-gating and feed expected inputs to the decoder, which goes against your idea of simple CLI I believe.
Would love @atanmarko / @muursh, and more generally anyone's opinion on this. Maybe there's another approach getting rid of feature-gating that would be more satisfactory with respect to the points above, but given the flexibility we need the different packages to have, which will keep on increasing as we get informed of additional requirements, etc.., I really think that obfuscating everything internally behind gates so that these custom pieces of logic are abstracted away is the less harmful way.
Hi Robin, Could you help me understand your objection better here? As a note, I have no problems with a Representing our constraints as data, using the more powerful language of e.g To be clear, the benefits to developers and users of trace decoder of this change are:
The main point is that following an I also think this is a decision with a low architectural risk today. Whether @hratoanina's thinking gives us different design requirements for example, the change is trivial to make to trace decoder if we take this approach. |
I haven't extensively investigated the approach to be honest, but it does feel weird to me to bloat API with arguments that end up being specific to a given network, that we need to expose through some enums for all variants. FWIW,
The thing is, we will have different binaries anyway, because
To be fair, I think this is a non-issue. Our CI is currently pretty poor, and this issue you mentioned arised because we were not testing a feature we were supporting which is a big red flag. We shouldn't have any supported feature untested.
I would say this is a personal take here. I personally find your approach (taken as a whole, not just this limited example but applied to all the items I mentioned on slack), more confusing than internally handling them in a "silent" way. That being said, it's true that if your IDE greys out feature-specific code, this isn't ideal (although tweaking the default feature temporarily would alleviate the issue). |
I can mostly talk about the I can understand how no feature-gating makes for a cleaner approach, and for a mature product with a stabilized API and set of features maybe it could be considered, but while we're still actively on the features I can definitely say that feature-gating makes the work easier. I can't project myself in the future to see if this would make maintaining the code base harder long-term, but it doesn't feel like it either. |
@0xaatif What do you think of a middle ground, still explicitly highlighting the need for a specific network feature without clogging the API with multiple enums that end up being specific to only a small subset. Something like this: Original
#[allow(unused_variables)]
/// Performs all the pre-txn execution rules of the targeted network.
fn do_pre_execution<StateTrieT: StateTrie + Clone>(
block: &BlockMetadata,
ger_data: Option<(H256, H256)>,
storage: &mut BTreeMap<H256, StorageTrie>,
trim_storage: &mut BTreeMap<ethereum_types::H160, BTreeSet<TrieKey>>,
trim_state: &mut BTreeSet<TrieKey>,
state_trie: &mut StateTrieT,
) -> anyhow::Result<()> {
// Ethereum mainnet: EIP-4788
#[cfg(feature = "eth_mainnet")]
do_beacon_hook(
block.block_timestamp,
storage,
trim_storage,
block.parent_beacon_block_root,
trim_state,
state_trie,
)?;
#[cfg(feature = "cdk_erigon")]
do_scalable_hook(
block,
ger_data,
storage,
trim_storage,
trim_state,
state_trie,
)?;
Ok(())
} Proposed
/// Performs all the pre-txn execution rules of the targeted network.
fn do_pre_execution<StateTrieT: StateTrie + Clone>(
block: &BlockMetadata,
ger_data: Option<(H256, H256)>,
storage: &mut BTreeMap<H256, StorageTrie>,
trim_storage: &mut BTreeMap<ethereum_types::H160, BTreeSet<TrieKey>>,
trim_state: &mut BTreeSet<TrieKey>,
state_trie: &mut StateTrieT,
) -> anyhow::Result<()> {
// Ethereum mainnet: EIP-4788
if cfg!(feature = "eth_mainnet") {
return do_beacon_hook(
block.block_timestamp,
storage,
trim_storage,
block.parent_beacon_block_root,
trim_state,
state_trie,
);
}
if cfg!(feature = "cdk_erigon") {
return do_scalable_hook(
block,
ger_data,
storage,
trim_storage,
trim_state,
state_trie,
);
};
Ok(())
} This should also solve your IDE problem, as it keeps the blocks in the binary (even if the condition is compile-time evaluated) |
Not really my area, FWIW no issues from the |
Happy with the middle ground for now :) thanks robin! |
No description provided.