Skip to content

fix(engine-api): add engine_appendBatchSignature RPC for sync nodes#93

Merged
chengwenxi merged 2 commits intomainfrom
fix/add-append-batch-signature-rpc
Apr 13, 2026
Merged

fix(engine-api): add engine_appendBatchSignature RPC for sync nodes#93
chengwenxi merged 2 commits intomainfrom
fix/add-append-batch-signature-rpc

Conversation

@panos-xyz
Copy link
Copy Markdown
Contributor

@panos-xyz panos-xyz commented Apr 10, 2026

Summary

  • Add engine_appendBatchSignature no-op stub to the Morph L2 Engine API
  • Unblocks sync nodes that get stuck after switching from blocksync to consensus mode

Problem

After a sync node completes fast sync (blocksync), Tendermint switches it to consensus mode to follow the live chain. In consensus mode, it receives precommit votes from validators. When a vote carries a BatchHash, the consensus state machine calls AppendBlsDataengine_appendBatchSignature on the execution layer.

morph-reth doesn't implement this method, returning "Method not found". The morphnode's retryableError() treats this as retryable, triggering exponential backoff retries for up to 30 minutes per vote. This blocks the consensus goroutine entirely, preventing newL2Block from being called and halting block import.

Root Cause Trace

tendermint consensus/state.go:2288  addVote()
  └── vote.BlockID.BatchHash non-empty → AppendBlsData()
        └── node/core/batch.go:284 → retryClient.AppendBlsSignature()
              └── engine_appendBatchSignature RPC → "Method not found"
                    └── retryableError() == true → backoff.Retry (max 30min)
                          └── consensus goroutine blocked → no new blocks

Fix

For non-sequencer sync nodes, BLS batch signatures are not needed (only sequencers aggregate them for L1 batch submission via CommitBatch). A no-op implementation that returns Ok(()) immediately unblocks the consensus state machine.

Changes

File Change
crates/payload/types/src/params.rs Add BatchSignature type
crates/payload/types/src/lib.rs Export BatchSignature
crates/engine-api/src/api.rs Add append_batch_signature default no-op to trait
crates/engine-api/src/rpc.rs Register engine_appendBatchSignature JSON-RPC endpoint
README.md Update Engine API method table

Test plan

  • cargo test -p morph-engine-api -p morph-payload-types — 60 tests pass
  • cargo clippy -p morph-engine-api -- -D warnings — clean
  • Deploy rebuilt binary and verify sync node resumes block import after switching to consensus mode

Summary by CodeRabbit

  • New Features

    • Added engine_appendBatchSignature Engine API method that accepts BLS batch signatures and batch hashes from the consensus layer. Compatible with both sequencer and sync node configurations.
  • Documentation

    • Updated Engine API documentation to include the new appendBatchSignature method and its behavior specifications.

…nodes

The consensus layer (morphnode) calls engine_appendBatchSignature on the
execution layer when it receives precommit votes that carry a BatchHash.
This happens after blocksync completes and the node switches to consensus
mode to follow the live chain.

Without this method, morph-reth returns "Method not found", which the
retryClient treats as retryable (retryableError returns true for everything
except DiscontinuousBlockError). The exponential backoff runs for up to
30 minutes per vote, blocking the tendermint consensus goroutine and
preventing any new blocks from being imported.

For non-sequencer sync nodes, storing BLS batch signatures is unnecessary
(only sequencers use them for L1 batch submission via CommitBatch), so a
no-op implementation is sufficient.

Changes:
- Add BatchSignature type to morph-payload-types
- Add append_batch_signature default no-op to MorphL2EngineApi trait
- Register engine_appendBatchSignature JSON-RPC endpoint in rpc.rs
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

This PR extends the Engine API and RPC interface with a new append_batch_signature method to handle BLS batch signatures from the consensus layer. A corresponding BatchSignature type is introduced to represent signature data containing signer information and signature bytes. Documentation is updated to reflect the new API method.

Changes

Cohort / File(s) Summary
API & RPC Interface
crates/engine-api/src/api.rs, crates/engine-api/src/rpc.rs
Added new async method append_batch_signature to MorphL2EngineApi trait with a no-op default implementation; added corresponding JSON-RPC handler with debug logging and error handling.
Type Definitions
crates/payload/types/src/params.rs, crates/payload/types/src/lib.rs
Introduced BatchSignature struct with signer, signer_pub_key, and signature fields; re-exported new type from params module.
Documentation
README.md
Added entry for new engine_appendBatchSignature method to Engine API table.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • anylots
  • chengwenxi

Poem

🐰 A signature arrives upon the blockchain's stage,
Batch-signed wisdom from consensus's sage,
The Engine now listens to cryptographic grace,
While sync nodes smile at their no-op embrace! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly describes the main change: adding the engine_appendBatchSignature RPC method to the engine-api for sync nodes. It is concise, specific, and directly reflects the primary objective of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/add-append-batch-signature-rpc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/payload/types/src/params.rs (1)

73-87: Add serde round-trip coverage for BatchSignature.

This new RPC-facing type should have explicit JSON shape tests to prevent accidental wire-format regressions.

Proposed test additions
 #[cfg(test)]
 mod tests {
     use super::*;
+    use alloy_primitives::Address;

+    #[test]
+    fn test_batch_signature_serde_roundtrip() {
+        let sig = BatchSignature {
+            signer: Address::from([0x11; 20]),
+            signer_pub_key: Bytes::from(vec![0x01, 0x02, 0x03]),
+            signature: Bytes::from(vec![0xAA, 0xBB, 0xCC]),
+        };
+
+        let json = serde_json::to_string(&sig).expect("serialize");
+        let decoded: BatchSignature = serde_json::from_str(&json).expect("deserialize");
+        assert_eq!(sig, decoded);
+    }
+
+    #[test]
+    fn test_batch_signature_camel_case_fields() {
+        let json = r#"{
+            "signer": "0x1111111111111111111111111111111111111111",
+            "signerPubKey": "0x010203",
+            "signature": "0xaabbcc"
+        }"#;
+        let parsed: BatchSignature = serde_json::from_str(json).expect("deserialize");
+        assert_eq!(parsed.signer, Address::from([0x11; 20]));
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/payload/types/src/params.rs` around lines 73 - 87, Add serde
round-trip unit tests for the BatchSignature type: create tests that construct a
BatchSignature (populating signer, signer_pub_key, and signature with
deterministic sample values), serialize it to JSON using serde_json::to_string,
assert the JSON shape/fields are as expected, then deserialize back with
serde_json::from_str and assert equality with the original via PartialEq.
Reference the BatchSignature struct and its fields (signer, signer_pub_key,
signature) and add tests alongside other payload types tests so any future
wire-format changes are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/engine-api/src/rpc.rs`:
- Around line 191-195: The debug message for appendBatchSignature is misleading
because it claims a "no-op for sync node" while the method then calls
self.inner.append_batch_signature(...); update the tracing::debug call (the log
string tied to %batch_hash) to not assert it's a no-op — instead log that the
RPC was received and is being forwarded to self.inner.append_batch_signature (or
that behavior may vary by implementation), so the message accurately reflects
the actual action performed.

---

Nitpick comments:
In `@crates/payload/types/src/params.rs`:
- Around line 73-87: Add serde round-trip unit tests for the BatchSignature
type: create tests that construct a BatchSignature (populating signer,
signer_pub_key, and signature with deterministic sample values), serialize it to
JSON using serde_json::to_string, assert the JSON shape/fields are as expected,
then deserialize back with serde_json::from_str and assert equality with the
original via PartialEq. Reference the BatchSignature struct and its fields
(signer, signer_pub_key, signature) and add tests alongside other payload types
tests so any future wire-format changes are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52334c55-abda-4903-bfc5-ed6cfa148a59

📥 Commits

Reviewing files that changed from the base of the PR and between 53ed544 and 063eb2f.

📒 Files selected for processing (5)
  • README.md
  • crates/engine-api/src/api.rs
  • crates/engine-api/src/rpc.rs
  • crates/payload/types/src/lib.rs
  • crates/payload/types/src/params.rs

Comment on lines +191 to +195
tracing::debug!(
target: "morph::engine",
%batch_hash,
"RPC appendBatchSignature called (no-op for sync node)"
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Log message is misleading for non-no-op implementations.

Line 194 says this RPC call is a no-op, but Line 197 forwards to self.inner.append_batch_signature(...), which may do real work in overridden implementations.

Suggested logging tweak
         tracing::debug!(
             target: "morph::engine",
             %batch_hash,
-            "RPC appendBatchSignature called (no-op for sync node)"
+            "RPC appendBatchSignature called"
         );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tracing::debug!(
target: "morph::engine",
%batch_hash,
"RPC appendBatchSignature called (no-op for sync node)"
);
tracing::debug!(
target: "morph::engine",
%batch_hash,
"RPC appendBatchSignature called"
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/engine-api/src/rpc.rs` around lines 191 - 195, The debug message for
appendBatchSignature is misleading because it claims a "no-op for sync node"
while the method then calls self.inner.append_batch_signature(...); update the
tracing::debug call (the log string tied to %batch_hash) to not assert it's a
no-op — instead log that the RPC was received and is being forwarded to
self.inner.append_batch_signature (or that behavior may vary by implementation),
so the message accurately reflects the actual action performed.

Copy link
Copy Markdown
Contributor

@chengwenxi chengwenxi left a comment

Choose a reason for hiding this comment

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

lgtm

@chengwenxi chengwenxi merged commit c319960 into main Apr 13, 2026
12 checks passed
@chengwenxi chengwenxi deleted the fix/add-append-batch-signature-rpc branch April 13, 2026 04:19
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.

2 participants