Skip to content

feat(#22): decode Bytes<64> secp256k1 validators on ledger-9.1 - #38

Merged
ctoyan merged 3 commits into
midnight-v1from
feat/22-real-ecdsa
Jul 15, 2026
Merged

feat(#22): decode Bytes<64> secp256k1 validators on ledger-9.1#38
ctoyan merged 3 commits into
midnight-v1from
feat/22-real-ecdsa

Conversation

@ctoyan

@ctoyan ctoyan commented Jul 13, 2026

Copy link
Copy Markdown

What

Teach the Midnight chain adapter to decode the real-ECDSA multisig ISM
state on the current Midnight node. Two coupled changes forced this:

  1. The ISM now enrolls validators as 64-byte secp256k1 public keys
    (X_be || Y_be), not 20-byte addresses.
  2. The node serializes contract state with the contract-state[v8] wire
    tag (onchain-state 4.0.0); the adapter was pinned to the ledger-8
    crates that only understand [v6], so every ISM / message read failed
    to deserialize.

Changes

  • ledger-9.1 migration. Repoint the native-decode crates at the
    ledger-9.1 git tags (not on crates.io), unified with a
    [patch.crates-io] block; the node serializes VerifierKeys with
    transient-crypto, so that is wire-matched.
  • Toolchain -> 1.96.1. The ledger crates need >=1.95; 1.97 breaks
    a transitive size-of 0.1.5, so size-of is vendored + patched to
    drop target-invalid fn-ptr ABIs.
  • Bytes<64> pubkey decode (state_decode.rs, ism.rs): read the
    validator set as 64-byte pubkeys; derive each 20-byte address as
    keccak256(pubkey)[12..] for the agent's signature-sorting bookkeeping.
  • MAX_SIGNATURES 16 -> 4 (mailbox.rs), matching the on-chain
    MAX_VALIDATORS; forward the quorum-sized signature set unpadded.
  • Configurable submit timeout (toolkit.rs) via
    MIDNIGHT_SUBMIT_TIMEOUT_SECS.
  • Fixture tests #[ignore]'d pending regeneration for the v8 / 64-byte
    registry format.

Testing

Validated end-to-end against a local Midnight devnet: an inbound
EVM->Midnight delivery ran the real multisig ISM handle proof
(keccak256 digest + secp256k1EcdsaVerify) to completion and credited
the recipient, using agents built from this branch.

On-chain merkle tree removed (folded into this PR)

Mirrors the contract-side removal (equilibriumco/hyperlane-midnight#89): the MerkleTreeHook no longer reads an on-chain root. count / latest_checkpoint derive from the off-chain IncrementalMerkle reconstructed from dispatched_messages (the exact root the validator already signs), so the local-vs-anchor cross-check stays self-consistent (no false reorg panic). The state_decode paths are re-derived for the new night layout (ISM [1,1-4], mailbox [1,8-10]; no more count/current_root slot) and night-state-dispatched.hex is regenerated. Motivation: the on-chain tree had grown the outbound proving key to 2.63 GiB, trapping at the 2 GiB wasm32 wall so outbound never proved.

Pre-removal state preserved on branch archive/onchain-merkle-tree in both repos (still hits the wall — outbound does not prove there).

ctoyan added 3 commits July 13, 2026 13:16
The Midnight multisig ISM enrolls validators as 64-byte secp256k1 public
keys (SEC1 uncompressed body X_be || Y_be) rather than 20-byte addresses,
and the current node serializes contract state with the
contract-state[v8] wire tag (onchain-state 4.0.0) that the ledger-8
adapter could not read. This migrates the native-decode path and teaches
it the new validator format.

- Repoint the native-decode crates at the ledger-9.1 git tags (not
  published on crates.io), unified via [patch.crates-io]; the patch
  wire-matches the node's VerifierKey serialization (transient-crypto).
- Toolchain -> 1.96.1: the ledger crates need >=1.95, but 1.97 breaks a
  transitive size-of 0.1.5, so size-of is vendored and patched to drop
  target-invalid fn-ptr ABIs.
- state_decode / ism: read the validator set as Bytes<64> pubkeys and
  derive each 20-byte address as keccak256(pubkey)[12..] for the agent's
  signature-sorting bookkeeping.
- mailbox: MAX_SIGNATURES 16 -> 4 to match the on-chain MAX_VALIDATORS
  cap; forward the quorum-sized signature set unpadded.
- toolkit: configurable submit timeout via MIDNIGHT_SUBMIT_TIMEOUT_SECS.
- Mark the state-decode fixture tests #[ignore] pending regeneration for
  the v8 / 64-byte registry format.
size-of 0.1.5 (a transitive Starknet build-dep) declares target-invalid fn-ptr ABIs that rustc >= 1.95 rejects (E0570); the ledger-9.1 decode graph forces rustc >= 1.95 (sysinfo 0.39 / cfg_select), so it must be patched. Replace the in-repo vendored copy with a Cargo [patch] pointing at equilibriumco/size-of (fork of 0.1.5 with only the target-invalid ABIs dropped). Removes the ~30 vendored files; same functional fix.
Mirror the contract-side removal of the on-chain merkle tree: MerkleTreeHook no
longer reads an on-chain root. count/latest_checkpoint derive from the off-chain
IncrementalMerkle reconstructed from dispatched_messages (the exact root the
validator already signs), so the local-vs-anchor cross-check stays self-consistent
(no false reorg panic). Re-derive state_decode paths for the new night layout
(ISM [1,1-4], mailbox [1,8-10]; no more count/current_root slot) and regenerate
night-state-dispatched.hex. Pre-removal state preserved on branch
archive/onchain-merkle-tree.
@ctoyan
ctoyan merged commit 9d005b6 into midnight-v1 Jul 15, 2026
51 of 56 checks passed
@ctoyan
ctoyan deleted the feat/22-real-ecdsa branch July 15, 2026 14:28
@ctoyan ctoyan self-assigned this Jul 15, 2026
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