Skip to content

fix: Remove engine api, add monitor handle and refactor full link monitor logic#107

Merged
Vui-Chee merged 14 commits intodevfrom
niven/refactor-engine-api
Jan 23, 2026
Merged

fix: Remove engine api, add monitor handle and refactor full link monitor logic#107
Vui-Chee merged 14 commits intodevfrom
niven/refactor-engine-api

Conversation

@sieniven
Copy link
Contributor

@sieniven sieniven commented Jan 21, 2026

Summary

This PR refactors and resolves the issues mentioned in PR #103.

  1. Remove the custom engine-api handler
  2. Better use of the engine consensus events and built payload stream to receive payload builder triggers / engine state updates
  3. Remove option from the underlying OpEngineApi inner handle, adds the new custom XLayerEngineApiBuilder which explicitly ensures that an instance of OpEngineApi handle is constructed on compile time
  4. Separate out full link custom monitoring logic into the separate xlayer-monitor crate, which contains the XLayerMonitor struct which encapsulates all full link monitoring logic
  5. The XLayerMonitor struct exposes interface that (to be added in the future @LeoGuo621) encapsulates logic for full link monitoring requirements
  6. Add the monitor handle, which is a worker thread for handling new engine events and payload events
  7. Add flashblocks enabled flag

@sieniven sieniven changed the title Refactor engine api, separate monitor crate fix: Refactor engine api, separate and better encapsulate full link monitor logic Jan 21, 2026
let provider = self.provider;

// Listen to both Attributes and BuiltPayload events in a single task
task_executor.spawn_critical(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should reduce the number of tasks being spawned for the monitor logic, into just a single task

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 3ea6d22

async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
// Initialize consensus engine events listener to track block received and canonical chain events
let consensus_listener = ConsensusListener::new(self.monitor.clone());
consensus_listener.listen(ctx.engine_events.new_listener(), ctx.node.task_executor());
Copy link
Contributor Author

@sieniven sieniven Jan 22, 2026

Choose a reason for hiding this comment

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

Dont think we should spawn another worker task here, now we are essentially running 2 tasks for not much reason (engine and payload listeners).

Also, running the task here doesnt make sense since this is nested inside an add on wrapper struct? This logic doesnt quite fit launch_add_ons functionality and i suggest we completely do away with this design. we should put this instantiation of a single monitor handle task, that uses tokio runtime to async await both from the engine event and payload receivers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 3ea6d22

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I dont really see a point in creating a custom struct for this. We just need the engine event receiver? Why create another nested level of custom add on definition here

Copy link
Contributor Author

@sieniven sieniven Jan 22, 2026

Choose a reason for hiding this comment

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

We do not need to create a custom XLayerAddOns to retrieve the engine_event by wrapping the launch_add_ons. A more elegant solution is resolved in 3ea6d22, where we can obtain the engine event listener after the node is contructed inside the NodeHandle.add_ons_handle


/// Handle transaction commits to the canonical chain.
pub fn on_tx_commit(&self, _block_info: &BlockInfo, _tx_hash: B256) {
info!(target: "xlayer::monitor", "monitor: Transaction committed to canonical chain");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do not spam the node with so much info level loggings. Setting the target to xlayer::monitor is sufficient, and we should set these to debug level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

/// Handle block building start event (when payload attributes are received).
/// This is triggered when forkchoiceUpdated contains payload attributes.
pub fn on_block_build_start(&self, block_number: u64) {
info!(target: "xlayer::monitor", block_number, "monitor: Block building started");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

if let Ok(payload_events) = payload_builder.subscribe().await {
// Use built_payload_stream to get accurate block numbers
let mut built_payload_stream = payload_events.into_built_payload_stream();
info!(target: "xlayer::monitor", "monitor: Payload events listener started");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same here. Is there a need to even log this out also actually? Lets change all of these to debug level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

task_executor.spawn_critical(
"xlayer-monitor-consensus-events",
Box::pin(async move {
info!(target: "xlayer::monitor", "monitor: Consensus engine events listener started");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that you dont need to prefix your logs with a monitor: since we already set the target to xlayer::monitor. thats what the rust log targets help us with, together with allowing us to specify log target levels when running.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

Comment on lines +81 to +96
Ok(None) => {
warn!(
target: "xlayer::monitor",
payload_id = %payload_id,
parent_hash = %parent_hash,
"monitor: Parent block not found for payload attributes"
);
}
Err(e) => {
warn!(
target: "xlayer::monitor",
payload_id = %payload_id,
parent_hash = %parent_hash,
error = %e,
"monitor: Failed to get parent block number"
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both are handled as logging, we can simply handle the match arm with _ =>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In addition I dont think we should spam the node logs here if somehow there is a state provider failure. We should set this to debug level - since if the parent hash cant be found, the engine state tree itself will already report error logs already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

// Call on_block_build_start with the block number
monitor.on_block_build_start(block_number);

info!(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why need to log again when monitor.on_block_build_start already adds a log?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

// Call on_block_send_start with the accurate block number
monitor.on_block_send_start(block_number);

info!(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same for this? on_block_send_start already adds a log?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in 62bde76

@sieniven sieniven force-pushed the niven/refactor-engine-api branch from 20c3e69 to 3ea6d22 Compare January 22, 2026 17:50
Comment on lines +11 to +16
pub struct BlockInfo {
/// The block number
pub block_number: u64,
/// The block hash
pub block_hash: B256,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use alloy NumHash instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolved in c31dd25

Comment on lines +71 to +75
trace!(
target: "xlayer::monitor::rpc",
"Transaction submission intercepted: method={}",
method_owned
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

trace log for transaction interception on the RPC layer is shifted inside the async task

@sieniven sieniven changed the title fix: Refactor engine api, separate and better encapsulate full link monitor logic fix: Remove engine api, add monitor handle and better encapsulate full link monitor logic Jan 22, 2026
@sieniven sieniven changed the title fix: Remove engine api, add monitor handle and better encapsulate full link monitor logic fix: Remove engine api, add monitor handle and refactor full link monitor logic Jan 22, 2026
@Vui-Chee Vui-Chee merged commit 58d4b6f into dev Jan 23, 2026
@Vui-Chee
Copy link
Contributor

lgtm, I see these improvements:

  • no need for engine-api crate
  • simpler event handling logic for different sub mobules
  • remove generic from middleware rpc

Vui-Chee added a commit that referenced this pull request Jan 23, 2026
* dev:
  fix: remove engine api, and simplify monitor logic (#107)
louisliu2048 pushed a commit that referenced this pull request Feb 5, 2026
* upgrade to v1.10.0 (#98)

* perf(builder): use XLayerPayloadServiceBuilder to simplify main codes (#95)

Co-authored-by: Vui-Chee <vuicheesiew@gmail.com>
Co-authored-by: Niven <sieniven@gmail.com>

* drop unused dep

* chore: better rename, fix unused variable warning (#99)

* Better refactor, fix unused var warn

* Better rename

* Remove xlayer suffix

* Align cargo chef base image with latest rust toolchain version (#100)

* Update builder version (#101)

* feat(full trace): Add full trace hook framework (#103)

* add engine api hook for full trace

* rm comment

* fix clippy

* add send tx hook

* add blockchain tracer

* cargo lock

* fix review issues

* simplify to tracer to avoid nasty type bonanza

* renamed `TracerConfig` to `Tracer`

* more renames

* EngineApiTracer derive clone over manual impl

* handle middleware overwrite problem

* reorder

* drop clone

* no box on middleware rpc tracer

* impl `EngineApiBuilder` directly to tracer

---------

Co-authored-by: Vui-Chee <vuicheesiew@gmail.com>

* fix: remove engine api, and simplify monitor logic (#107)

* remove unused reth-node-api (#108)

* fix: resolve audit issues related to flashblocks subscription (#113)

* Fix unnecessary clone

* Fix incomplete documentation of subscribe addrs filter

* Better desc

* chore(flashblocks): update builder version to v0.2.5 (#114)

* Update builder version to v0.2.5 with full link monitor updates

* Fix wspub init

* feat: Add Transaction Trace Monitoring Support (#115)

* add trace logic

* optimize

* fix ut

* fix ut

* fix

* fix

* optimize code

* optimize code

* fix

* chore: fix rust formatter (#132)

* Fix fmt

* Remove unused dep

* Fix nested if else clippy check

* Update dependencies, use reth v1.10.2 and builder v0.3.0 (#133)

* fix: upgrade reth v1.10.2 version with bug fixes (#134)

* Fix revm dep

* Update reth version, use merge fixes version

* Add audit report (#135)

* add audit report

* add audit report

---------

Co-authored-by: chunsheng.wang <chunsheng.wang@okg.com>

* feat: add flashblocks enabled RPC method (#136)

* feat: add flashblocks enabled RPC method

* chore: clippy

* chore: add unit tests

* chore: reword documentation

* remove min gas rpc (#138)

---------

Co-authored-by: jimmyshi <417711026@qq.com>
Co-authored-by: Niven <sieniven@gmail.com>
Co-authored-by: Leo <335209779@qq.com>
Co-authored-by: wangchunsheng-ops <13614269216@163.com>
Co-authored-by: chunsheng.wang <chunsheng.wang@okg.com>
Co-authored-by: lucas <66681646+limyeechern@users.noreply.github.com>
louisliu2048 pushed a commit that referenced this pull request Feb 5, 2026
* upgrade to v1.10.0 (#98)

* perf(builder): use XLayerPayloadServiceBuilder to simplify main codes (#95)

Co-authored-by: Vui-Chee <vuicheesiew@gmail.com>
Co-authored-by: Niven <sieniven@gmail.com>

* drop unused dep

* chore: better rename, fix unused variable warning (#99)

* Better refactor, fix unused var warn

* Better rename

* Remove xlayer suffix

* Align cargo chef base image with latest rust toolchain version (#100)

* Update builder version (#101)

* feat(full trace): Add full trace hook framework (#103)

* add engine api hook for full trace

* rm comment

* fix clippy

* add send tx hook

* add blockchain tracer

* cargo lock

* fix review issues

* simplify to tracer to avoid nasty type bonanza

* renamed `TracerConfig` to `Tracer`

* more renames

* EngineApiTracer derive clone over manual impl

* handle middleware overwrite problem

* reorder

* drop clone

* no box on middleware rpc tracer

* impl `EngineApiBuilder` directly to tracer

---------

Co-authored-by: Vui-Chee <vuicheesiew@gmail.com>

* fix: remove engine api, and simplify monitor logic (#107)

* remove unused reth-node-api (#108)

* fix: resolve audit issues related to flashblocks subscription (#113)

* Fix unnecessary clone

* Fix incomplete documentation of subscribe addrs filter

* Better desc

* chore(flashblocks): update builder version to v0.2.5 (#114)

* Update builder version to v0.2.5 with full link monitor updates

* Fix wspub init

* feat: Add Transaction Trace Monitoring Support (#115)

* add trace logic

* optimize

* fix ut

* fix ut

* fix

* fix

* optimize code

* optimize code

* fix

* chore: fix rust formatter (#132)

* Fix fmt

* Remove unused dep

* Fix nested if else clippy check

* Update dependencies, use reth v1.10.2 and builder v0.3.0 (#133)

* fix: upgrade reth v1.10.2 version with bug fixes (#134)

* Fix revm dep

* Update reth version, use merge fixes version

* Add audit report (#135)

* add audit report

* add audit report

---------

Co-authored-by: chunsheng.wang <chunsheng.wang@okg.com>

* feat: add flashblocks enabled RPC method (#136)

* feat: add flashblocks enabled RPC method

* chore: clippy

* chore: add unit tests

* chore: reword documentation

* remove min gas rpc (#138)

---------

Co-authored-by: jimmyshi <417711026@qq.com>
Co-authored-by: Niven <sieniven@gmail.com>
Co-authored-by: Leo <335209779@qq.com>
Co-authored-by: wangchunsheng-ops <13614269216@163.com>
Co-authored-by: chunsheng.wang <chunsheng.wang@okg.com>
Co-authored-by: lucas <66681646+limyeechern@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants