Skip to content

[DRAFT]: LayerZero Message Processing#1442

Draft
TarekkMA wants to merge 52 commits intomasterfrom
tarekkma/lz-processor
Draft

[DRAFT]: LayerZero Message Processing#1442
TarekkMA wants to merge 52 commits intomasterfrom
tarekkma/lz-processor

Conversation

@TarekkMA
Copy link

@TarekkMA TarekkMA commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • LayerZero-based message routing and forwarding between relay and container chains
    • Receiver pallet on container chains for inbound LayerZero messages
    • In-chain APIs to configure per-chain routing and send outbound LayerZero messages
  • Integration

    • LayerZero message processor added to inbound queue handling
    • XCM barrier extended to permit unpaid execution from the parent/relay chain
  • Tests

    • End-to-end and unit tests covering forwarding, whitelisting, and error scenarios
  • Documentation

    • Added LayerZero integration architecture and usage guide

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Adds LayerZero support: new pallets pallet-lz-router and pallet-lz-receiver-example, integrates them into relay and container runtimes, implements a LayerZero v2 message processor with fallback handling, extends primitives/types and XCM configuration for unpaid execution from parent, and adds end-to-end and unit tests plus test tooling and TS API augmentations.

Changes

Cohort / File(s) Summary
Workspace deps
Cargo.toml, chains/runtime-common/Cargo.toml
Add pallet-lz-router and pallet-lz-receiver-example workspace deps and std/runtime-bench/try-runtime feature wiring.
New pallet: LzRouter
pallets/lz-router/Cargo.toml, pallets/lz-router/src/lib.rs, pallets/lz-router/src/types.rs, pallets/lz-router/src/mock.rs, pallets/lz-router/src/tests.rs
New pallet providing routing config storage, Config trait (MaxWhitelistedSenders, ContainerChainOrigin, ConvertLocation), events/errors, dispatchables (update_routing_config, send_message_to_ethereum), handle_inbound_message forwarding via XCM, plus mock and tests.
New pallet: LzReceiverExample
pallets/lz-receiver-example/Cargo.toml, pallets/lz-receiver-example/src/lib.rs
Receiver example pallet exposing receive_message callable gated by ParentOrigin; emits MessageReceived and enforces parent origin.
Runtime templates — container chains
chains/container-chains/runtime-templates/simple/..., .../frontier/... (Cargo.toml, src/lib.rs, src/xcm_config.rs)
Register LzReceiverExample in both simple/frontier runtimes (pallet index 79), add ParentLocation param and include AllowExplicitUnpaidExecutionFrom<Equals<ParentLocation>> in XcmBarrier.
Relay runtime (dancelight)
chains/orchestrator-relays/runtime/dancelight/Cargo.toml, .../src/lib.rs, .../src/xcm_config.rs, .../src/bridge_to_ethereum_config.rs
Add pallet-lz-router dependency and runtime pallet (index 127); provide Config impl for LzRouter; add LayerZeroInboundMessageProcessorV2 alias and include XcmPassthrough in LocalOriginConverter.
Runtime-common processors
chains/runtime-common/src/processors/v2/*
Add LayerZeroMessageProcessor with try_extract/process_message and MessageProcessorWithFallback impl; rename SymbioticFallbackProcessor → PrivilegedFallbackProcessor; treat non-Xcm raw payloads as unsupported; add RawPayload::LayerZero variant and module export.
Bridge primitives
primitives/bridge/src/inbound_queue/layerzero_message.rs, primitives/bridge/src/inbound_queue/mod.rs
New LayerZero inbound message types, MAGIC_BYTES, aliases (LayerZeroAddress/Endpoint), InboundMessage struct and conversion from SOL payload.
XCM config & barriers
chains/container-chains/.../src/xcm_config.rs, chains/orchestrator-relays/runtime/dancelight/src/xcm_config.rs
Expose ParentLocation = Location::parent(), allow explicit unpaid execution from ParentLocation, add Xcm passthrough origin and LocalOriginConverter adjustments; add LzRouter runtime Config wiring.
Tests & test infra
test/*, test/utils/*, test/helpers/*, test/suites/*, test/configs/*
Add extensive tests: unit/integration for processors and LzRouter, E2E zombie environment for LayerZero forwarding, helpers for event assertions, light-client LayerZero payload encoding, XCM helpers (sovereignAccountOfChildForAddress32, sendCallAsChildPara), new moonwall environment, and TS API augmentations.
TypeScript API augmentations
typescript-api/src/dancelight/interfaces/*, typescript-api/.../types-lookup.ts, registry.ts
Add lzRouter consts/errors/events/queries/txs/lookup types and inbound message types, replacing/augmenting prior beefy entries in lookups.
Docs
docs/bridge/layerzero-integration.md
New architecture documentation describing LayerZero integration, flows, message formats, and configuration guidance.

Sequence Diagram(s)

sequenceDiagram
    participant Eth as Ethereum (Snowbridge)
    participant Relay as Relay Chain (Dancelight)
    participant Proc as LayerZeroProcessor
    participant Router as pallet_lz_router
    participant XCM as XCM Router
    participant Container as Container Chain
    participant Receiver as LzReceiverExample

    Eth->>Relay: Ethereum inbound -> EthereumInboundQueueV2
    Relay->>Proc: submit Message -> try_extract_message
    alt extract succeeds
        Proc->>Router: process_message(LayerZeroInboundMessage)
        Router->>Router: validate routing config & whitelist
        Router->>XCM: send Transact XCM to Container
        XCM->>Container: deliver Transact
        Container->>Receiver: call receive_message (Transact)
        Receiver->>Receiver: ensure ParentOrigin & emit MessageReceived
    else extract fails
        Proc->>Proc: delegate to PrivilegedFallbackProcessor (asset trap / fallback)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

A8-mergeoncegreen, B7-runtimenoteworthy, D9-needsaudit👮, R8-orchestratorsolo, R7-container, V-1700

Suggested reviewers

  • Agusrodri
  • evgeny-s
  • tmpolaczyk

Poem

🐰 Hopped a log from Ethereum’s shore,

Routed a packet, then hopped some more,
Relay, router, XCM in tow,
To container chains the messages go,
A happy rabbit cheers: “Forward, hop, and score!” 🎉

Pre-merge checks and finishing touches

✅ 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 '[DRAFT]: LayerZero Message Processing' is directly related to the main changeset, which introduces comprehensive LayerZero infrastructure including message processors, router pallet, and receiver example pallet.
Docstring Coverage ✅ Passed Docstring coverage is 96.72% which is sufficient. The required threshold is 80.00%.

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.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 30, 2025

WASM runtime size check:

Compared to target branch

dancebox runtime: 1888 KB (-4 KB) ✅

flashbox runtime: 1120 KB (no changes) ✅

dancelight runtime: 2696 KB (+16 KB) 🚨

starlight runtime: 2588 KB (+4 KB) 🚨

container chain template simple runtime: 1532 KB (+16 KB) ⚠️

container chain template frontier runtime: 1856 KB (-4 KB) ✅

@github-actions
Copy link
Contributor

github-actions bot commented Dec 30, 2025

Coverage Report

(master)

@@                    Coverage Diff                    @@
##           master   tarekkma/lz-processor      +/-   ##
=========================================================
+ Coverage   72.93%                  73.12%   +0.19%     
+ Files         569                     575       +6     
+ Lines       81064                   81722     +658     
=========================================================
+ Hits        59124                   59757     +633     
+ Misses      21940                   21965      +25     
Files Changed Coverage
/chains/container-chains/runtime-templates/simple/src/weights/frame_system.rs 9.09% (+9.09%)
/chains/container-chains/runtime-templates/simple/src/weights/xcm/mod.rs 41.11% (+4.75%)
/chains/container-chains/runtime-templates/simple/src/weights/xcm/pallet_xcm_benchmarks_generic.rs 25.89% (+6.25%)
/chains/runtime-common/src/processors/v2/symbiotic_message_processor.rs 80.52% (+1.03%)

Coverage generated

@TarekkMA
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_symbiotic_update.ts (1)

87-87: Assertion appears incorrect - missing equality check.

expect(externalValidatorsList, validators) passes validators as an optional message parameter to expect(), not as the expected value. This test will always pass regardless of the actual validators list.

🔎 Proposed fix
-                expect(externalValidatorsList, validators);
+                expect(externalValidatorsList).toEqual(validators);
🧹 Nitpick comments (17)
pallets/lz-router/Cargo.toml (1)

31-31: Consider making frame-benchmarking optional.

frame-benchmarking is included as a regular dependency, meaning it will always be compiled even in non-benchmark builds. This contrasts with pallet-lz-receiver-example/Cargo.toml which correctly marks it as optional.

🔎 Proposed fix
-frame-benchmarking = { workspace = true }
+frame-benchmarking = { workspace = true, optional = true }

And update the std feature:

-	"frame-benchmarking/std",
+	"frame-benchmarking?/std",
pallets/lz-receiver-example/Cargo.toml (1)

24-27: Dev-dependencies shouldn't be in std feature list.

sp-core and sp-io are declared as dev-dependencies (lines 25-26), but their /std features are included in the std feature array (lines 36-37). Dev-dependencies are only available during tests, so referencing them in the main std feature could cause build issues in certain configurations.

🔎 Proposed fix

Remove dev-dependency std features from the main std array:

 std = [
 	"frame-benchmarking?/std",
 	"frame-support/std",
 	"frame-system/std",
 	"parity-scale-codec/std",
 	"scale-info/std",
-	"sp-core/std",
-	"sp-io/std",
 	"sp-runtime/std",
 	"xcm/std",
 ]
pallets/lz-router/src/lib.rs (4)

131-132: Placeholder weights need proper benchmarks.

Weight::from_parts(10_000, 0) is a placeholder that doesn't reflect actual execution cost. The PoV (proof-of-value) component is 0, which may undercharge for storage reads.

Consider adding a TODO or implementing proper weight benchmarks before production deployment:

// TODO: Add proper weight benchmarks for this call
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]

161-162: Same placeholder weight issue for send_message_to_ethereum.

This extrinsic also uses placeholder weights. Since it's a stub (TODO on line 173), this is acceptable for now but should be addressed when implementation is complete.


173-174: TODO stub for Ethereum outbound queue integration.

The send_message_to_ethereum function currently only emits an event without actual message queuing. This is noted as a TODO, which is appropriate for a draft PR.

Would you like me to help outline the integration points with the Snowbridge outbound queue when you're ready to implement this?


83-85: Remove #[pallet::without_storage_info] — not needed.

MessageForwardingConfig<T> explicitly derives MaxEncodedLen, so the pallet can generate storage info without this attribute. This attribute should only be used when storage types don't implement MaxEncodedLen.

test/helpers/pallets.ts (1)

1-1: Avoid @ts-nocheck - fix the underlying type issue instead.

Disabling all type checking for this file defeats the purpose of using TypeScript. The type issue is likely related to the metadata API types.

🔎 Suggested fix with proper typing
-// @ts-nocheck
-
 import type { DevModeContext } from "@moonwall/cli";
 
 /**
  * Get the pallet index for a given pallet name
  */
 export const getPalletIndex = async (palletName: string, context: DevModeContext): Promise<number> => {
     const metadata = await context.polkadotJs().rpc.state.getMetadata();
     const pallets = metadata.asLatest.pallets;
-    const pallet = pallets.find((p) => p.name.toString() === palletName);
+    const pallet = pallets.find((p: { name: { toString(): string } }) => p.name.toString() === palletName);
     if (!pallet) {
         throw new Error(`Pallet ${palletName} not found`);
     }
-    return pallet.index.toNumber();
+    return (pallet as { index: { toNumber(): number } }).index.toNumber();
 };

If the types are complex, consider using a targeted // @ts-expect-error with an explanation on the specific problematic line rather than disabling checking for the entire file.

chains/orchestrator-relays/runtime/dancelight/src/lib.rs (1)

2071-2076: LzRouter pallet registration is aligned; consider follow-up integration points

Registering LzRouter: pallet_lz_router = 127 fits the existing index layout and is sufficient to surface the pallet’s calls. As you evolve this pallet, consider whether it should also:

  • Be reachable via specific ProxyType variants / governance origins.
  • Be allowed or restricted under MaintenanceFilter.
  • Get weight/benchmark coverage added to the benches module.

These are follow‑ups rather than blockers.

chains/orchestrator-relays/runtime/dancelight/src/tests/processors/v2/layerzero_message_processor.rs (1)

1-401: LayerZero extraction tests give solid coverage of success and failure paths

The test module sets up realistic LayerZero ABI payloads and exercises LayerZeroMessageProcessor::try_extract_message across the happy path and key invalid cases (origin, assets/value, magic bytes, ABI decoding, RawPayload decoding, wrong variant). This is a good safety net around the extraction logic.

If you touch this again, you might factor out the repeated Message { .. } construction into a small helper to keep the tests a bit drier, but it’s not necessary for correctness.

test/configs/zombie_tanssi_relay_layerzero.json (1)

55-118: Consider using distinct Prometheus ports per collator

All collators for parachain 2000 share prometheus_port: 33102 (including FullNode-2000). This can lead to port binding conflicts when nodes run on the same host. If these are intended to be separate processes, assigning unique Prometheus ports per node would be safer.

test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message_fails.ts (1)

25-49: Negative-path LZ tests are good; keep error/name and indices in sync

Both failure cases (no config and non‑whitelisted sender) are well covered: you allow the inbound extrinsic to fail, then assert on decoded dispatch error strings, which should catch regressions in the router/processor logic. Two small points to keep in mind:

  • retrieveDispatchErrors matches by substring ("NoForwardingConfig", "NotWhitelistedSender"), so any renaming of error variants will require updating these tests.
  • As in the success test, you configure notificationDestination: [100, 0]; if the intent is to eventually notify the LZ receiver pallet on the container chain, verify that these indices match the actual (pallet_index, call_index) there.

Also applies to: 51-85, 88-135

pallets/lz-receiver-example/src/lib.rs (1)

71-80: Pallet design is minimal and correct; a couple of small optional tweaks

The example pallet cleanly enforces an XCM origin (via ParentOrigin) and then double‑checks that the Location is the parent with is_parent, before emitting a MessageReceived event. The call’s weight is conservative and appropriate for an event‑only extrinsic.

Two optional refinements you might consider later:

  • If ParentOrigin is always configured as EnsureXcm<Equals<ParentLocation>>, the extra is_parent check becomes redundant; either rely solely on the origin type or relax ParentOrigin and keep the explicit check.
  • If you expect larger payloads, you could switch the event field to a BoundedVec<u8, MaxMessageSize> tied to the inbound queue’s max size to make the bound explicit.

Also applies to: 84-94, 131-143

test/suites/zombie_tanssi_relay_layerzero/test_layerzero_forwarding.ts (2)

1-1: Consider removing @ts-nocheck if possible.

Disabling TypeScript checking for the entire file can mask type errors. If specific lines have type issues, consider using @ts-expect-error with explanations on those specific lines instead.


138-139: Hardcoded call index may be fragile.

The callIndex = 0 is hardcoded assuming receive_message is the first call in the pallet. Consider deriving this from metadata similar to how palletIndex is derived, which would make the test more resilient to pallet changes.

🔎 Suggested approach

You could query the metadata to find the call index programmatically:

// Example: find call index from metadata
const lzReceiverCalls = containerMetadata.asLatest.pallets
    .find(({ name }) => name.toString() === "LzReceiverExample")
    ?.calls.unwrap().type;
// Then lookup the "receive_message" call index from the type registry
primitives/bridge/src/inbound_queue/layerzero_message.rs (1)

56-68: Consider defensive handling instead of expect() in From implementation.

While the comment notes that lzSourceAddress is always 32 bytes (matching bytes32 in Solidity), using expect() in a From implementation can cause panics if the invariant is ever violated. Consider using TryFrom instead, or at minimum, ensure this conversion is only called in contexts where the size is already validated.

🔎 Alternative with TryFrom
-impl From<SolMessage> for Message {
-    fn from(sol_message: SolMessage) -> Self {
+impl TryFrom<SolMessage> for Message {
+    type Error = &'static str;
+    
+    fn try_from(sol_message: SolMessage) -> Result<Self, Self::Error> {
         Self {
             lz_source_address: sol_message
                 .lzSourceAddress
                 .to_vec()
                 .try_into()
-                .expect("lzSourceAddress is always 32 bytes; qed"),
+                .map_err(|_| "lzSourceAddress must be 32 bytes")?,
             lz_source_endpoint: sol_message.lzSourceEndpoint,
             destination_chain: sol_message.destinationChain,
             message: sol_message.message.into(),
         }
     }
 }
test/helpers/expect.ts (1)

1-1: Consider reducing @ts-nocheck scope.

Similar to the test file, @ts-nocheck disables all TypeScript checking. While the complex generic types may require some flexibility, consider using targeted @ts-expect-error comments (which are already present in some places) to maintain type safety where possible.

test/utils/xcm.ts (1)

13-13: Remove unnecessary console import.

The console object is a Node.js global and doesn't need to be explicitly imported. This import can be safely removed.

🔎 Proposed fix
-import * as console from "node:console";
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccc761f and 42c154a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • Cargo.toml
  • chains/container-chains/runtime-templates/frontier/Cargo.toml
  • chains/container-chains/runtime-templates/frontier/src/lib.rs
  • chains/container-chains/runtime-templates/frontier/src/xcm_config.rs
  • chains/container-chains/runtime-templates/simple/Cargo.toml
  • chains/container-chains/runtime-templates/simple/src/lib.rs
  • chains/container-chains/runtime-templates/simple/src/xcm_config.rs
  • chains/orchestrator-relays/runtime/dancelight/Cargo.toml
  • chains/orchestrator-relays/runtime/dancelight/src/bridge_to_ethereum_config.rs
  • chains/orchestrator-relays/runtime/dancelight/src/lib.rs
  • chains/orchestrator-relays/runtime/dancelight/src/tests/processors/v2/layerzero_message_processor.rs
  • chains/orchestrator-relays/runtime/dancelight/src/tests/processors/v2/mod.rs
  • chains/orchestrator-relays/runtime/dancelight/src/xcm_config.rs
  • chains/runtime-common/Cargo.toml
  • chains/runtime-common/src/processors/v2/fallback_message_processor.rs
  • chains/runtime-common/src/processors/v2/layerzero_message_processor.rs
  • chains/runtime-common/src/processors/v2/mod.rs
  • chains/runtime-common/src/processors/v2/raw_message_processor.rs
  • chains/runtime-common/src/processors/v2/symbiotic_message_processor.rs
  • chains/runtime-common/src/relay.rs
  • pallets/lz-receiver-example/Cargo.toml
  • pallets/lz-receiver-example/src/lib.rs
  • pallets/lz-router/Cargo.toml
  • pallets/lz-router/src/lib.rs
  • pallets/lz-router/src/types.rs
  • primitives/bridge/src/inbound_queue/layerzero_message.rs
  • primitives/bridge/src/inbound_queue/mod.rs
  • test/configs/zombie_tanssi_relay_layerzero.json
  • test/helpers/expect.ts
  • test/helpers/index.ts
  • test/helpers/pallets.ts
  • test/moonwall.config.json
  • test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message.ts
  • test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message_fails.ts
  • test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_symbiotic_update.ts
  • test/suites/zombie_tanssi_relay_layerzero/test_layerzero_forwarding.ts
  • test/tsconfig.json
  • test/utils/light-client.ts
  • test/utils/xcm.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-12T17:11:07.511Z
Learnt from: tmpolaczyk
Repo: moondance-labs/tanssi PR: 1420
File: chains/runtime-common/src/processors/v2/proc_macro/Cargo.toml:1-19
Timestamp: 2025-12-12T17:11:07.511Z
Learning: Ensure that Cargo.toml files target Rust 1.85+ when using edition = "2024". Before bumping edition to 2024, verify the project is built with Rust 1.85.0 or newer and that all dependencies support the new edition. If upgrading, add or update the line: edition = "2024" in the Cargo.toml under [package], and test compilation and feature compatibility across the workspace.

Applied to files:

  • chains/runtime-common/Cargo.toml
  • Cargo.toml
  • chains/container-chains/runtime-templates/simple/Cargo.toml
  • chains/orchestrator-relays/runtime/dancelight/Cargo.toml
  • pallets/lz-router/Cargo.toml
  • pallets/lz-receiver-example/Cargo.toml
  • chains/container-chains/runtime-templates/frontier/Cargo.toml
📚 Learning: 2025-12-16T15:08:22.484Z
Learnt from: tmpolaczyk
Repo: moondance-labs/tanssi PR: 1420
File: Cargo.toml:548-548
Timestamp: 2025-12-16T15:08:22.484Z
Learning: In the moondance-labs/tanssi repo, rely on toml-maid to enforce TOML formatting. Do not raise review issues for whitespace or formatting differences in TOML files that toml-maid does not flag. Focus reviews on substantive content changes; only flag formatting that toml-maid would flag or that indicates a real TOML syntax error.

Applied to files:

  • chains/runtime-common/Cargo.toml
  • Cargo.toml
  • chains/container-chains/runtime-templates/simple/Cargo.toml
  • chains/orchestrator-relays/runtime/dancelight/Cargo.toml
  • pallets/lz-router/Cargo.toml
  • pallets/lz-receiver-example/Cargo.toml
  • chains/container-chains/runtime-templates/frontier/Cargo.toml
🧬 Code graph analysis (7)
test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message_fails.ts (4)
test/helpers/starlightVersions.ts (1)
  • STARLIGHT_VERSIONS_TO_EXCLUDE_FROM_SNOWBRIDGE_V2 (21-21)
test/utils/xcm.ts (1)
  • sendCallAsChildPara (1174-1270)
test/utils/light-client.ts (1)
  • generateLayerZeroOutboundLog (294-309)
test/helpers/events.ts (1)
  • retrieveDispatchErrors (50-71)
test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_symbiotic_update.ts (2)
test/utils/light-client.ts (1)
  • generateSymbioticOutboundLog (315-326)
chains/orchestrator-relays/runtime/dancelight/src/lib.rs (1)
  • validators (1473-1475)
chains/orchestrator-relays/runtime/dancelight/src/tests/processors/v2/layerzero_message_processor.rs (2)
chains/runtime-common/src/processors/v2/fallback_message_processor.rs (1)
  • processors (111-111)
chains/runtime-common/src/processors/v2/mod.rs (3)
  • into (93-107)
  • from (60-62)
  • try_extract_message (373-376)
test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message.ts (3)
test/helpers/starlightVersions.ts (1)
  • STARLIGHT_VERSIONS_TO_EXCLUDE_FROM_SNOWBRIDGE_V2 (21-21)
test/utils/xcm.ts (2)
  • getChildParaSovereignAccount (1156-1164)
  • sendCallAsChildPara (1174-1270)
test/utils/light-client.ts (2)
  • generateLayerZeroOutboundLog (294-309)
  • generateUpdate (442-563)
chains/runtime-common/src/processors/v2/fallback_message_processor.rs (1)
chains/runtime-common/src/processors/v2/mod.rs (1)
  • handle_message (366-366)
primitives/bridge/src/inbound_queue/layerzero_message.rs (1)
chains/runtime-common/src/processors/v2/mod.rs (1)
  • from (60-62)
test/utils/xcm.ts (1)
test/utils/block.ts (1)
  • jumpToSession (31-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: check-unused-dependencies
  • GitHub Check: build
  • GitHub Check: cargo-toml-feature-propagation
  • GitHub Check: build-and-coverage
🔇 Additional comments (44)
primitives/bridge/src/inbound_queue/mod.rs (1)

17-22: LGTM - New module declaration is appropriate.

The layerzero_message module is correctly declared as public. Note that unlike the other modules, it's not re-exported via pub use. This appears intentional for more explicit imports of LayerZero types.

test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_symbiotic_update.ts (2)

10-10: Import refactor looks good.

The switch from generateOutboundMessageAcceptedLog to generateSymbioticOutboundLog properly abstracts the symbiotic-specific defaults.


72-72: Simplified function call is cleaner.

The new signature generateSymbioticOutboundLog(polkadotJs, 1, validators) is more readable and hides the symbiotic-specific defaults internally.

chains/container-chains/runtime-templates/frontier/src/xcm_config.rs (2)

107-109: New ParentLocation parameter is well-defined.

The parameter correctly defines the parent chain location using Location::parent().


117-134: Unpaid execution from parent chain is security-sensitive but appropriately scoped.

Adding AllowExplicitUnpaidExecutionFrom<Equals<ParentLocation>> allows the parent/orchestrator chain to send XCM messages that execute without paying fees. This is correctly scoped only to the parent location and positioned before WithComputedOrigin for proper evaluation order. Configuration is consistent across both frontier and simple runtime templates.

Security consideration: Ensure the parent chain (relay/orchestrator) is fully trusted, as it can now dispatch arbitrary unpaid execution. The UnpaidExecution instruction in the forwarded XCM (from pallet-lz-router) relies on this barrier passing.

chains/orchestrator-relays/runtime/dancelight/src/tests/processors/v2/mod.rs (1)

35-37: LGTM - Test module addition follows existing pattern.

The new layerzero_message_processor test module is correctly declared alongside the existing processor test modules.

chains/orchestrator-relays/runtime/dancelight/Cargo.toml (1)

145-145: Dependency addition is correct.

pallet-lz-router is properly added as a workspace dependency.

pallets/lz-router/Cargo.toml (1)

33-35: Verify necessity of pallet-session dependency.

pallet-session with historical feature is included, but reviewing the pallet source (pallets/lz-router/src/lib.rs), there's no apparent usage of session-related functionality. This may be copy-paste from another pallet's Cargo.toml.

pallets/lz-router/src/lib.rs (1)

217-227: Remove the double encoding concern; the encoding is intentional and correct for XCM Transact format.

The call construction (pallet_index, call_index, message.encode()).encode() is correct. The XCM Transact instruction extracts the pallet and call indices from the call field, then decodes the remaining bytes as function arguments. Since receive_message expects payload: Vec<u8>, the remaining bytes must be decodable as a Vec, which requires a length prefix. Encoding the tuple produces exactly this: pallet_index byte + call_index byte + (length prefix) + message bytes.

The Unlimited unpaid execution with check_origin: None is acceptable since the parent chain barrier is trusted, but this assumption should be documented.

chains/container-chains/runtime-templates/frontier/Cargo.toml (1)

33-33: LGTM!

The pallet-lz-receiver-example dependency is properly integrated with all required feature flags (std, runtime-benchmarks, try-runtime), following the established workspace dependency patterns.

Also applies to: 194-194, 271-271, 321-321

chains/runtime-common/src/relay.rs (1)

51-56: LGTM!

The LayerZeroMessageProcessor is properly added to the v2 public re-exports, following the existing pattern and correctly gated behind the relay feature flag.

test/helpers/index.ts (1)

6-7: LGTM!

New helper modules are properly re-exported, following the existing barrel export pattern.

test/moonwall.config.json (2)

52-52: LGTM!

Adding RUST_LOG=lz=debug enables LayerZero-specific debug logging for the dev environment, which is helpful for development and debugging.


503-565: New zombie test environment looks well-structured.

The zombie_tanssi_relay_layerzero configuration follows existing patterns and includes appropriate setup for LayerZero E2E testing. The referenced config file ./configs/zombie_tanssi_relay_layerzero.json exists and is included in this PR.

chains/runtime-common/src/processors/v2/raw_message_processor.rs (1)

99-103: LGTM!

Using a wildcard pattern _ to handle non-Xcm RawPayload variants is appropriate here, as LayerZero and other message types should be processed by their dedicated processors. This provides forward compatibility for additional payload variants.

test/helpers/pallets.ts (1)

8-16: Function logic is correct.

The implementation properly retrieves pallet metadata and finds the index by name. The error handling for missing pallets is appropriate.

chains/runtime-common/Cargo.toml (2)

20-20: LGTM!

alloy-core with sol-types feature is appropriate for LayerZero message handling which involves Solidity/Ethereum type encoding.


69-69: LGTM!

pallet-lz-router is properly integrated as an optional dependency with correct feature flag propagation:

  • Optional in dependencies
  • Gated behind relay feature
  • Proper ? suffix usage for conditional feature propagation in std, runtime-benchmarks, and try-runtime

Also applies to: 95-95, 129-129, 148-148, 186-186

Cargo.toml (1)

90-91: Workspace entries for LayerZero pallets are consistent

Both pallet-lz-router and pallet-lz-receiver-example follow the existing pattern (path-based, default-features = false) and integrate cleanly into [workspace.dependencies]. No issues from a workspace/layout perspective.

chains/runtime-common/src/processors/v2/mod.rs (1)

20-24: LayerZero processor wiring and RawPayload extension look correct

Exposing layerzero_message_processor and appending RawPayload::LayerZero(Vec<u8>) as the last variant keeps SCALE encoding compatible with existing variants and fits the existing processor re-export pattern.

Also applies to: 110-115

chains/container-chains/runtime-templates/simple/Cargo.toml (1)

30-30: LZ receiver example pallet is correctly wired into the simple template

pallet-lz-receiver-example is added as a workspace dependency and consistently enabled for std, runtime-benchmarks, and try-runtime, mirroring other pallets in this runtime template. This looks coherent and ready for runtime integration.

Also applies to: 151-152, 225-225, 271-272

chains/container-chains/runtime-templates/simple/src/xcm_config.rs (1)

49-57: XCM barrier change to allow explicit unpaid exec from parent is coherent with LayerZero routing

Introducing ParentLocation and adding AllowExplicitUnpaidExecutionFrom<Equals<ParentLocation>> into XcmBarrier cleanly allows explicitly‑marked unpaid XCM from the relay/orchestrator while keeping the rest of the barrier unchanged. That’s consistent with using the parent as a trusted entry point for forwarded messages (e.g. LayerZero).

Please just re‑confirm this matches your threat model (i.e., the parent may execute unpaid XCM here and cannot be adversarial in your deployment).

Also applies to: 100-102, 109-126

chains/container-chains/runtime-templates/frontier/src/lib.rs (2)

59-61: Trait imports are consistent with later usage

Newly imported traits (Equals, InsideBoth, Contains, InstanceFilter, etc.) are all exercised later in call filters and XCM wiring; no issues spotted.


1111-1113: LzReceiverExample wiring looks correct; verify ParentLocation definition

The pallet_lz_receiver_example::Config implementation and construct_runtime! registration (index 79) are consistent and match the pallet’s expectations (Parent-origin via XCM and (pallet_index, call_index) encoding). Just ensure that xcm_config::ParentLocation is exactly the relay/parent location (parents = 1, interior = Here) so it aligns with the pallet’s is_parent check.

Also applies to: 1161-1161

chains/orchestrator-relays/runtime/dancelight/src/xcm_config.rs (2)

135-144: XcmPassthrough addition is appropriate for XCM-aware pallets

Adding pallet_xcm::XcmPassthrough<RuntimeOrigin> to LocalOriginConverter cleanly exposes pallet_xcm::Origin::Xcm to the runtime, which is required for pallets (like the LZ router) that need to see the raw XCM origin. This fits the existing XCM config and barrier setup.


176-181: LZ router origin and location conversion look sound

OnlyParachains correctly constrains ContainerChainOrigin to Locations of the form (parents = 0, [Parachain(_)]), and LocationConverter already handles these locations. The MaxWhitelistedSenders bound is reasonable. No issues seen with the new pallet_lz_router::Config wiring.

Please double‑check that all expected container parachains present themselves with parents = 0 in XCM origins (and not e.g. via another consensus layer), otherwise OnlyParachains may be too strict.

Also applies to: 357-361

chains/orchestrator-relays/runtime/dancelight/src/bridge_to_ethereum_config.rs (1)

46-50: LayerZero inbound processor wiring is consistent with existing processors

The LayerZeroInboundMessageProcessorV2 alias mirrors the type parameters of the existing Raw and Symbiotic processors, and adding it as the third entry in the MessageProcessor tuple for snowbridge_pallet_inbound_queue_v2::Config looks structurally correct. As long as can_process_message for Raw/Symbiotic/LayerZero are mutually exclusive or ordered intentionally, this integration should behave as expected.

It’s worth confirming in tests that a LayerZero message is actually handled by LayerZeroInboundMessageProcessorV2 and not accidentally claimed by the Raw or Symbiotic processors first.

Also applies to: 752-783, 793-797

test/suites/dev-tanssi-relay/ethereum-inbound-queue-v2/test_receive_layerzero_message.ts (1)

25-49: Setup (runtime detection and funding) is reasonable

Runtime detection/skip for specific Starlight specVersions and funding the para 2000 sovereign account via transferAllowDeath align with the other Snowbridge V2 tests; no issues here.

chains/container-chains/runtime-templates/simple/src/lib.rs (2)

53-55: New trait imports align with filters and XCM usage

Adding Contains, Equals, InsideBoth, and InstanceFilter matches their use in proxy filters, maintenance filters, and XCM execution manager; nothing problematic here.


914-916: LzReceiverExample integration looks consistent with other runtimes

The pallet_lz_receiver_example::Config implementation and runtime registration at index 79 mirror the frontier template, with ParentOrigin constrained to EnsureXcm<Equals<xcm_config::ParentLocation>>. This is consistent with the example pallet’s expectations.

Just confirm xcm_config::ParentLocation is the same parent location the relay uses when sending LayerZero notifications.

Also applies to: 958-958

pallets/lz-router/src/types.rs (1)

17-55: LGTM!

The type definitions are well-structured with appropriate documentation. The use of BoundedVec for whitelisted_senders ensures bounded storage, and the derive macros are correctly applied for a storage-compatible configuration struct. The skip_type_params(T) attribute properly handles the generic parameter for scale-info.

chains/runtime-common/src/processors/v2/fallback_message_processor.rs (1)

164-250: LGTM!

The rename from SymbioticFallbackProcessor to PrivilegedFallbackProcessor is appropriate as this processor now serves both Symbiotic and LayerZero protocols. The conditional fallback logic correctly differentiates between user errors (trap assets for recovery) and middleware errors (return error to signal the problem).

chains/runtime-common/src/processors/v2/symbiotic_message_processor.rs (1)

19-21: LGTM!

The updates correctly integrate the renamed PrivilegedFallbackProcessor and simplify the match arm by using a wildcard pattern for non-Symbiotic payloads. This maintains the same behavior while being more concise and future-proof for additional payload types.

Also applies to: 86-89, 210-229

test/suites/zombie_tanssi_relay_layerzero/test_layerzero_forwarding.ts (1)

55-348: Comprehensive E2E test coverage.

The test suite thoroughly covers the LayerZero forwarding flow from relay to container chain, including:

  • Pallet verification on both relay and container chains
  • XCM-based forwarding config setup with proper sovereign account funding
  • LayerZero message submission and relay chain processing
  • DMP delivery verification on the container chain
chains/runtime-common/src/processors/v2/layerzero_message_processor.rs (2)

40-100: Well-structured LayerZero message processor.

The implementation follows the established pattern from SymbioticMessageProcessor with appropriate validation:

  • Origin verification against gateway proxy
  • Asset/value checks to prevent asset-containing messages
  • Magic bytes validation for message authenticity
  • Clean delegation to pallet_lz_router for forwarding

The use of PrivilegedFallbackProcessor as the fallback type is consistent with the generalized fallback approach.

Also applies to: 102-106, 108-202


70-76: No changes needed; ABI decode mutable slice usage is correct.

The abi_decode_validate method requires a mutable reference to track buffer position during deserialization, consistent with how the Decode trait is used throughout the codebase (e.g., Decode::decode(&mut signature.as_ref())). The pattern &mut payload.as_slice() is idiomatic for codec operations that consume bytes sequentially.

Likely an incorrect or invalid review comment.

primitives/bridge/src/inbound_queue/layerzero_message.rs (1)

24-29: LGTM!

The LayerZero message types are well-defined with appropriate MAGIC_BYTES for payload verification. The sol! macro correctly defines the Solidity-compatible structures, and the Rust Message struct provides a clean interface with proper derives for encoding/decoding.

Also applies to: 31-45, 47-53

test/utils/light-client.ts (2)

118-121: Verify address padding for non-20-byte inputs.

The padding logic assumes lzSourceAddress needs left-padding to 32 bytes. For a 20-byte Ethereum address, this produces 12 leading zero bytes + 20 address bytes, which is correct. However, if the input is already 32 bytes or has unexpected length, the behavior may be incorrect:

  • If lzSourceAddress.length > 32: slice(0, 32) truncates correctly
  • If lzSourceAddress.length < 32: 32 - length gives positive offset, padding left

This looks correct for the intended use case (Ethereum addresses to bytes32).


109-129: Well-designed LayerZero test utilities.

The additions follow the existing patterns for XCM and Symbiotic payloads. The LayerZeroMessageParams interface provides a clean API, and the convenience functions (generateLayerZeroOutboundLog, generateSymbioticOutboundLog) reduce boilerplate in tests while maintaining flexibility through the underlying generateOutboundMessageAcceptedLog.

Also applies to: 131-135, 164-170, 187-204, 290-326

test/helpers/expect.ts (1)

10-39: Useful test assertion helpers.

The expectOk, expectSubstrateEvent, expectSubstrateEvents, and expectSystemEvent helpers provide good abstractions for common test patterns. The error messages include helpful context about which events were found when assertions fail.

Also applies to: 41-97, 99-130, 141-156

test/utils/xcm.ts (4)

178-190: LGTM - but note duplicate function below.

The implementation correctly computes the sovereign account using the "para" prefix convention. However, this function is duplicated at lines 1156-1164 as getChildParaSovereignAccount.


466-479: Verify the omission of maxAssets with customBeneficiary.

When customBeneficiary is provided, the function creates a DepositAsset instruction without the maxAssets field, while the legacy path (lines 487 and 500) includes it. The max_assets parameter is also ignored in this code path.

Ensure this behavior is intentional and aligns with the XCM specification for the version being used.


663-675: LGTM!

The as_v4() and as_v5() methods correctly mirror the existing as_v3() pattern and enable versioned XCM message emission for V4 and V5.


1174-1270: LGTM with note on circular dependency pattern.

This comprehensive helper function correctly orchestrates child-to-relay XCM message flows for E2E testing. The dynamic import of jumpToSession (line 1185) is a reasonable workaround for circular dependencies.

The validation logic properly searches through blocks to verify message processing, and the hard-coded weight values are appropriate for test utilities.

Consider refactoring the module structure in a future change to eliminate the need for dynamic imports, which can make dependencies less transparent.

@TarekkMA
Copy link
Author

/cmd generate-ts-api

@TarekkMA
Copy link
Author

/cmd generate-ts-api

…nd improve error handling. Updated message type to BoundedVec for size constraints and modified conversion logic to return specific errors for oversized messages.
… and OutboundSolMessageEnvelope for better clarity. Updated related payload handling and ensured consistent naming conventions across the codebase.
// Expected responses are OK.
AllowKnownQueryResponses<PolkadotXcm>,
// Allow unpaid execution from the parent chain (for LayerZero forwarded messages)
AllowExplicitUnpaidExecutionFrom<Equals<ParentLocation>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Remember that we still need to discuss if we actually want this barrier in containers or not. Maybe add a TODO for it?

Copy link
Author

Choose a reason for hiding this comment

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

Okay will leave this comment up, until we decide on what we will do.

XcmProcessor: ExecuteXcm<<T as pallet_xcm::Config>::RuntimeCall>,
XcmWeigher: WeightBounds<<T as pallet_xcm::Config>::RuntimeCall>,
{
type Fallback = PrivilegedFallbackProcessor<
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this is a good fallback. There is this code in PrivilegedFallbackProcessor:

// If origin is not gateway proxy, a user mistakenly or maliciously sent privileged message
// If origin is gateway proxy, the privileged middleware sent the message with wrong semantics
// Based on above assumption we do conditional fallback
if message.origin != GatewayAddress::get() {
AssetTrapFallbackProcessor::handle_message(who, message)
} else {
Err(MessageProcessorError::ProcessMessage(DispatchError::Other(
"Invalid privileged message payload",
)))
}

In LayerZeroMessageProcessor the origin is always the gateway, but it is a user initiated message. So the assumption that gateway origin == privileged message is no longer true, and the fallback processor will return an error instead of trapping the assets.

But, LayerZeroMessageProcessor enforces that the message has no assets and no value in try_extract_message. And the fallback is executed only if try_extract_message returns ok and process_message returns err. So trapping assets is not needed.

There is no "AlwaysErrorFallbackProcessor", but maybe that's what we need here. Or just ignore the error and set a "NoopFallbackProcessor"?

I don't remember what was the problem with returning an error in the fallback processor so I don't know the difference, do you remember @Agusrodri ?

Copy link
Contributor

Choose a reason for hiding this comment

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

According to the fallback processor docs:

/// This processor always returns success to prevent reverting the Ethereum transaction,
/// which would leave assets in limbo on the Ethereum side. If XCM execution fails,
/// the error is logged but the processor still returns success, allowing the claimer
/// to recover the trapped assets later.

Copy link
Author

@TarekkMA TarekkMA Jan 9, 2026

Choose a reason for hiding this comment

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

I discussed with @ParthDesai about this case. when layerzero message is sent from TanssiLzHub (Eth) to the Gateway contract, we will override the origin to be the gateway. It will be treated as symbiotic update

Comment on lines +54 to +61
if message.origin != gateway_proxy_address {
return Err(MessageExtractionError::InvalidMessage {
context: format!(
"LayerZero message origin is {:?} expected {:?}",
message.origin, gateway_proxy_address
),
source: None,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

So related to @tmpolaczyk comment, we should modify this section as well right?

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