Conversation
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
|
@claude pls review this |
XLayer-Bot
left a comment
There was a problem hiding this comment.
Overall the architecture is clean and the injection pattern (wrapping OpEvmFactory / swapping the precompile map) is a solid approach for adding custom precompiles without forking upstream. A few issues need to be addressed before this can ship.
| /// Input format: N * 32 bytes (N field elements) | ||
| /// Output format: 32 bytes (one field element) | ||
| fn poseidon_hash(input: &[u8]) -> Result<Bytes, PrecompileError> { | ||
| let _ = input; |
There was a problem hiding this comment.
Critical: poseidon_hash is not implemented.
fn poseidon_hash(input: &[u8]) -> Result<Bytes, PrecompileError> {
let _ = input;
Ok(Bytes::from(vec![0u8; 32])) // always returns 32 zero bytes
}The input is explicitly discarded and the function always returns 32 zero bytes. The poseidon-rs, num-bigint, and ff_ce crates are added to Cargo.toml but never imported or used anywhere. Any contract relying on 0x100 for a real Poseidon hash will silently receive wrong results. This needs to be wired up to an actual Poseidon implementation before being deployed.
| let num_inputs = if input.is_empty() { 0 } else { input.len() / 32 }; | ||
| let gas_cost = POSEIDON_BASE_GAS + (num_inputs as u64) * POSEIDON_PER_INPUT_GAS; | ||
|
|
||
| tracing::info!( |
There was a problem hiding this comment.
tracing::info! is too verbose for a precompile hot path. Every transaction targeting 0x100 emits two structured info-level log lines (entry + success). In production this creates significant log noise proportional to Poseidon call volume. Please lower these to tracing::trace! (or at most debug!).
| xlayer_with_poseidon() | ||
| } else { | ||
| // Use standard OP Stack precompiles. | ||
| Precompiles::latest() |
There was a problem hiding this comment.
Precompiles::latest() returns the most recent precompile set known to the current revm version, not the set that corresponds to a specific hardfork. For a node syncing in e.g. Holocene context, this silently enables precompiles from hardforks that haven't activated yet. The recommended approach is to map each hardfork to its snapshot (e.g. Precompiles::cancun(), Precompiles::prague() etc.). The same issue applies to line 35 inside xlayer_with_poseidon() where Precompiles::latest() is used as the base for the Jovian+ set.
| self, | ||
| ctx: &BuilderContext<Node>, | ||
| pool: Pool, | ||
| _evm_config: xlayer_evm::XLayerEvmConfig, |
There was a problem hiding this comment.
Flashblocks sequencer path discards XLayerEvmConfig.
async fn spawn_payload_builder_service(
self,
ctx: &BuilderContext<Node>,
pool: Pool,
_evm_config: xlayer_evm::XLayerEvmConfig, // unused — note the underscore
) -> ... {
let base_evm = OpEvmConfig::optimism(ctx.chain_spec()); // fresh stock config
FlashblocksServiceBuilder::spawn_payload_builder_service(self.0, ctx, pool, base_evm).await
}The XLayerEvmConfig (which carries the Poseidon precompile) is silently dropped and replaced with a vanilla OpEvmConfig. When running as a flashblocks sequencer, blocks are built without the Poseidon precompile registered, so any transaction calling 0x100 will fail during block building even though it passes during execution/validation. The XLayerEvmConfig should be passed through (or the flashblocks builder needs to be updated to support custom EVM configs).
| .with_add_ons(add_ons) | ||
| .with_components( | ||
| xlayer_node.components() | ||
| .executor(XLayerExecutorBuilder) // ← 使用自定义 Executor with Poseidon |
There was a problem hiding this comment.
Development comment with Chinese text () left in production code. Please remove or replace with an English comment if explanation is needed.
1. Background
XLayer is an Optimism-based L2 that requires custom precompiled contracts (Poseidon hash at address
0x100).The challenge is to integrate these custom precompiles into a Reth-based node without modifying upstream Reth code.
Why This Matters
Key Constraint
Reth's EVM configuration is tightly coupled through the
ConfigureEvmtrait. We need to inject customprecompiles at EVM creation time without touching the upstream
reth-optimism-evmcrate.2. Solution Architecture
Key Design Points
crates/evm/,crates/node/, andbin/node/XLayerEvmConfigwrapsOpEvmConfigwith a custom EVM factoryXLayerEvmFactorydelegates toOpEvmFactorythen swaps precompilesxlayer_precompiles()returns&'static Precompilesfor performanceOpHardfork::Jovianand later3. Key Code Call Stack
Startup Path (Node Launch → EVM Creation)
Transaction Execution Path (TX → Precompile)
Verification
Test the precompile with:
Or run the automated test: