Skip to content

test(#18): relayer message enumeration + MessageIdMultisig metadata parity - #35

Merged
ctoyan merged 2 commits into
midnight-v1from
feat/18-relayer-message-metadata-indexer
Jul 2, 2026
Merged

test(#18): relayer message enumeration + MessageIdMultisig metadata parity#35
ctoyan merged 2 commits into
midnight-v1from
feat/18-relayer-message-metadata-indexer

Conversation

@ctoyan

@ctoyan ctoyan commented Jul 2, 2026

Copy link
Copy Markdown

#18 — Relayer: message + metadata indexer for Midnight

Verification + test coverage for the relayer's outbound half (Midnight as ORIGIN). Research confirmed the relayer's message enumeration and MessageIdMultisigMetadataBuilder are entirely chain-agnostic — once the Midnight crate supplies the dispatch indexer (#16), merkle indexer (#15), chain-sourced ISM (#14), and ValidatorAnnounce (#33), and the validator signs standard checkpoints (#17), the relayer lists Midnight-origin messages and builds standard metadata with zero per-chain code. So #18 is verification + tests, same shape as #17 (no new production code).

What's here (offline, no node/prover)

  • indexer.rs::dispatch_enumerates_committed_fixture_in_sequence — decodes the real fix(cardano): prevent consumption of ref script UTXOs #16 fixture (night-state-dispatched.hex) through the production decode_dispatch_snapshot and drives the actual fetch_dispatch_logs range logic, asserting the relayer enumerates both dispatches with Indexed.sequence == nonce and that the messages are the fixture's decoded messages (same keccak ids the merkle indexer / validator anchor on). Covers AC 1 ("lists pending Midnight-origin messages").
  • metadata_tests.rs (new module) — asserts the assembled MessageIdMultisig metadata blob (merkleTreeHook || root || index(BE) || sigs) matches the 68 + 65*threshold byte layout a standard destination ISM reads, then runs the exact destination-side verification: recovers every signature and matches the validator set with a forward-only two-pointer, proving the signers are the ascending {0, 2} subset (index 1 skipped) and the blob is consumable unchanged by a stock EVM MessageIdMultisigIsm. Covers AC 2 ("consumable by standard ISM verifiers"). Drift-guards the committed hyperlane-metadata-vector.json against the fixture (root/index/messageId), same as fix(cardano): fix minted amount #17.

The metadata vector is generated offline by generate-metadata-vector.ts in the contracts repo (equilibriumco/hyperlane-midnight#78), using the same two-dispatch Compact-simulator scenario as the #17 checkpoint vector and the independent @hyperlane-xyz/utils digest oracle. Its root and validator-0 signature match #17's checkpoint vector byte-for-byte.

Caveats investigated and cleared (not on the relay path, no fix)

  • MidnightMailbox::count() returns Ok(0) — confirmed no production caller chain-wide; the relayer's origin sequence comes from the dispatch indexer's nonce_count.
  • The dispatch indexer reads one state snapshot per scan and serves the range from it (not a per-nonce fetch).

Large-host live outbound relayer run deferred to the outbound E2E (#26).

Tests

cargo test -p hyperlane-midnight: 70 passed, 0 failed. Clippy clean.

Closes #18.

ctoyan added 2 commits July 2, 2026 14:14
…arity

Verify the relayer's outbound half for Midnight-origin messages against
committed offline fixtures (no node/prover), following #17's methodology.

- indexer.rs: dispatch_enumerates_committed_fixture_in_sequence drives the
  real decode_dispatch_snapshot + fetch_dispatch_logs range logic over the
  #16 fixture, asserting enumeration with sequence == nonce.
- metadata_tests.rs (new): assert the assembled MessageIdMultisig metadata
  blob (merkleTreeHook || root || index(BE) || sigs) matches the byte layout
  a standard destination ISM reads, and run the exact forward-only two-pointer
  verification recovering an ascending M-of-N signer subset.
- hyperlane-metadata-vector.json: offline 2-of-3 metadata reference vector.
Address REVIEW-18 gaps #5 and #6:

- The signing subset is now descending by address while ascending by set
  index, so the forward-only two-pointer verification adversarially proves
  ordering is enforced by set index, not by address (the old {0,2} subset was
  ascending under both orderings). Adds an explicit descending-address assert.
- Assert digest parity (signing_hash == inner, eth_signed_message_hash ==
  digest) against the independent @hyperlane-xyz/utils oracle, consuming the
  previously-dead vector fields and localising a digest regression to a clear
  failure instead of a confusing signer-not-in-set.
@ctoyan ctoyan self-assigned this Jul 2, 2026
@ctoyan
ctoyan merged commit 1344521 into midnight-v1 Jul 2, 2026
50 of 54 checks passed
@ctoyan
ctoyan deleted the feat/18-relayer-message-metadata-indexer branch July 2, 2026 12:35
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.

1 participant