Files

22 KiB

Teleport Bridge - AI Assistant Documentation

Overview

lux/teleport is the PQ-signed bridge reference implementation for the Lux ecosystem. It defines the protocol — EIP-712 typed-data flow, claimId-based replay protection, role-separated MPC oracle, IAM-authenticated admin path, KMS-backed signer keys — and ships a minimal, audit-clean implementation that exercises every security primitive.

Production deployment lives in ~/work/lux/bridge: same protocol, same contracts, plus the multi-tenant SDK (@luxfi/bridge), the deployment binaries, the operator tooling, and the chain-specific manifests. If you are looking for the production bridge runtime, go there. If you are looking for the protocol spec, the security model, or the canonical threshold-signer abstraction, this is the right repo.

The two repos are kept aligned on:

  • Contracts: canonical sources in ~/work/lux/standard/contracts/bridge/, imported by both repos. lux/teleport/contracts/contracts/ is a reference mirror; lux/bridge consumes the standard repo via its contracts workspace.
  • MPC scheme abstraction: pluggable ThresholdSigner interface (mpc/src/signers/) with policyId() consumed by StrictPQProfileGate. lux/bridge reuses the same abstraction for its production oracle bus.
  • EIP-712 / claimId: identical CLAIM_TYPEHASH and replay-tracking rules. Cross-deployment claims are mechanically equivalent.

Reference vs production — read this first

Concern Reference (lux/teleport) Production (lux/bridge)
Audience protocol spec + minimal impl white-label tenants + operators
SDK none — direct contracts + scripts @luxfi/bridge (npm) + mountBridge()
Contracts contracts/contracts/ (mirror of standard/contracts/bridge/) imports standard/contracts/bridge/
MPC 3-node devnet topology 2-of-3 m-chain (user) + separate private MPC (treasury)
Auth dev creds IAM (OIDC) + JWKS validation through Lux Gateway
KMS dev keys Lux KMS (KMSSecret-projected volumes, never logged)
Deploy hardhat scripts k8s manifests + operator + GAR images
Versioning tracks protocol changes tracks deployment cadence

Both repos pin the same standard-contracts SHA. A protocol change lands in lux/standard first, then bumps in both lux/teleport (reference proof + tests) and lux/bridge (production rollout).

Security Architecture

Protocol Security

  1. EIP-712 Typed Data Signing: All signatures use structured EIP-712 format instead of string concatenation, preventing malleability and encoding mismatches.

  2. Destination Binding: The bridgeBurn() function commits destination data (toChainId, recipient, vault, nonce) into the on-chain event. Oracle nodes derive all claim fields from logs, never from request parameters.

  3. ClaimId-Based Replay Protection: Claims are tracked by claimId = keccak256(abi.encode(...all claim fields...)) instead of signature bytes, preventing ECDSA malleability exploits.

  4. Token Whitelisting: Only admin-approved tokens can be bridged, preventing malicious token contract attacks.

  5. Role Separation: Distinct ADMIN_ROLE, ORACLE_ROLE, and PAUSER_ROLE with proper access control.


Trustless Bridge Roadmap

Current: MPC Oracle Bridge (v1.1.0)

The current implementation uses MPC (Multi-Party Compute) oracles:

  • 2-of-3 threshold signatures
  • Oracle nodes watch for burn events
  • Sign claims for minting on destination chain
  • Trust assumption: Majority of MPC nodes are honest

Future: Trustless Light Client Bridge

For Ethereum ↔ Lux trustless bridging without MPC oracles:

1. Light Client Verification

Deploy light clients on each chain to verify the other chain's consensus:

On Lux (verify Ethereum):

contract EthereumLightClient {
    // Verify Ethereum beacon chain sync committee signatures
    // Update finalized headers every ~27 hours
    function verifyHeader(BeaconBlockHeader header, bytes[] signatures) external;
    function verifyStorageProof(bytes32 stateRoot, address account, bytes32 slot, bytes proof) external;
}

On Ethereum (verify Lux):

contract LuxLightClient {
    // Verify Lux validator BLS signatures (Quasar consensus)
    // Verify P-Chain validator set changes
    function verifyHeader(LuxBlockHeader header, bytes signature) external;
    function verifyReceiptProof(bytes32 receiptRoot, bytes proof) external;
}

2. Storage/Receipt Proofs

Instead of oracle signatures, users submit Merkle proofs:

function claimWithProof(
    bytes32 burnTxHash,
    bytes calldata receiptProof,     // Merkle proof of receipt inclusion
    bytes calldata headerProof,       // Proof header is finalized
    ClaimData calldata claim
) external {
    // 1. Verify header is finalized via light client
    require(lightClient.isFinalized(claim.sourceBlockHash));
    
    // 2. Verify receipt proof against receipts root
    bytes32 receiptRoot = lightClient.getReceiptsRoot(claim.sourceBlockHash);
    require(verifyReceiptProof(receiptRoot, receiptProof, claim.burnTxHash));
    
    // 3. Extract and verify burn event from receipt
    BurnEvent memory burn = decodeBurnEvent(receiptProof);
    require(burn.toChainId == block.chainid);
    require(burn.recipient == msg.sender);
    
    // 4. Mint tokens
    token.bridgeMint(burn.recipient, burn.amount);
}

3. ZK State Proofs

For faster finality and lower gas costs, use ZK proofs. The verifier plugged into IVerifier is profile-dependent: for off-chain bridges to a classical L1 EVM (Ethereum mainnet, etc.) a Groth16 or PLONK verifier is the cheap-gas option and is acceptable because the relying party is itself classical; for Lux Z-Chain anchor verification under LUX_STRICT_E2E_PQ the verifier MUST be a STARK backend (ProofPolicySTARKFRISHA3PQ) — Groth16 / PLONK / KZG are refused in that slot regardless of operator flag.

The Solidity surface for this is contracts/StrictPQProfileGate.sol, a one-function library (refuseUnderStrictPQ) that mirrors contract.RefuseUnderStrictPQ in ~/work/lux/node/quasar/contract. Verifiers declare their backend via IVerifier.policyId() and the gate decides whether the verifier is allowed in the active profile slot. Verification logic and policy enforcement stay decomplected: the verifier does its job, the gate decides admissibility.

ZK-SNARK Verification (classical-compat bridge to L1 EVM):

contract ZKBridge {
    IVerifier public verifier;  // Groth16 or Plonk verifier (classical-compat only;
                                // refused in the Z-Chain proof slot under strict-PQ)
    
    function claimWithZKProof(
        bytes calldata proof,        // ZK proof of state transition
        bytes32 publicInputHash,     // Hash of claim data
        ClaimData calldata claim
    ) external {
        // Verify succinct proof of:
        // 1. Source chain finality
        // 2. Burn event inclusion
        // 3. Correct claim derivation
        require(verifier.verify(proof, publicInputHash));
        
        token.bridgeMint(claim.recipient, claim.amount);
    }
}

Benefits:

  • Constant verification cost (~200-400k gas)
  • No trust assumptions (cryptographic security)
  • Fast finality (no waiting for light client updates)

4. Range Proofs for Privacy

For private bridging (amounts hidden):

contract PrivateBridge {
    // Pedersen commitments for amounts
    function bridgeBurnPrivate(
        address token,
        bytes32 amountCommitment,    // Pedersen commitment
        bytes calldata rangeProof,   // Bulletproof that amount > 0 and < 2^64
        uint256 toChainId,
        address recipient
    ) external {
        require(verifyRangeProof(amountCommitment, rangeProof));
        // Burn committed amount (ZK balance check)
    }
}

Implementation Priority

Phase Feature Trust Model Timeline
1.1.0 ✅ MPC Oracles (secure) 2-of-3 honest Current
1.2.0 Light Client Cryptographic Q2 2025
1.3.0 Receipt Proofs Trustless Q2 2025
1.4.0 ZK Proofs Trustless + Fast Q3 2025
1.5.0 Range Proofs Private + Trustless Q4 2025

Research Areas

  1. Ethereum Beacon Chain Light Client

    • Sync committee verification (512 validators)
    • ~27 hour finality for updates
    • See: EIP-4788, Lodestar
  2. Lux Consensus Verification

    • BLS multi-signatures from validators
    • P-Chain for validator set changes
    • Quasar consensus proofs
  3. ZK Circuits

    • Ethereum block header verification in circuit
    • MPT (Merkle Patricia Trie) proof verification
    • Receipt decoding and event extraction
  4. Cross-Chain Standards

    • ERC-7683: Cross Chain Intents
    • ERC-7786: Cross-Chain Messages
    • IBC (Cosmos) style proofs

Package Structure

teleport/
├── contracts/           # Solidity smart contracts
│   └── contracts/
│       ├── Bridge.sol         # Main bridge (EIP-712, role-based)
│       ├── ERC20B.sol         # Bridgeable token
│       └── IBridgeToken.sol   # Token interface
├── api/                 # Bridge API service
│   └── src/
│       └── bridge.ts          # POST endpoints, event parsing
├── mpc/                 # MPC signing service
│   └── src/
│       └── teleporter.ts      # Secure MPC signing
├── app/                 # Frontend UI
└── multi-party-ecdsa/   # MPC library

Smart Contracts

Bridge.sol

The main bridge contract with security features:

  • EIP-712 Domain: TeleportBridge version 1
  • CLAIM_TYPEHASH: For typed claim verification
  • Roles:
    • ADMIN_ROLE: Token whitelist, oracle management, fee config
    • ORACLE_ROLE: Granted to MPC signers
    • PAUSER_ROLE: Emergency pause capability

Key Functions:

// Burn tokens with committed destination (all data in event)
function bridgeBurn(
    address token,
    uint256 amount,
    uint256 toChainId,
    address recipient,
    bool vault
) external returns (bytes32 burnId);

// Mint tokens with EIP-712 signature verification
function bridgeMint(
    ClaimData calldata claim,
    bytes calldata signature
) external returns (bytes32 claimId);

BridgeBurned Event:

event BridgeBurned(
    bytes32 indexed burnId,
    address indexed token,
    address indexed sender,
    uint256 amount,
    uint256 toChainId,
    address recipient,
    bool vault,
    uint256 nonce
);

ERC20B.sol

Bridgeable token with role-based access:

  • BRIDGE_ROLE: Only bridge contracts can mint/burn
  • Pausable: Emergency token freeze
  • Proper event emissions

API Service (api/src/bridge.ts)

TypeScript service with security improvements:

Endpoints

  • POST /api/signature - Request claim signature
  • GET /api/claim/:claimId - Get claim status
  • GET /health - Health check

Security Features

  1. POST Only: Sensitive data in request body, not URL
  2. Event Parsing: All claim fields derived from on-chain logs
  3. Rate Limiting: 100 requests per 15 minutes per IP
  4. Helmet: Security headers
  5. Input Validation: Strict hex format validation

Request/Response

// Request
interface SignatureRequest {
  burnTxHash: string;   // 0x-prefixed 32-byte hex
  fromChainId: number;  // Source chain ID
  logIndex: number;     // Event log index
}

// Response
interface SignatureResponse {
  claimId: string;
  burnTxHash: string;
  logIndex: number;
  token: string;
  amount: string;
  toChainId: number;
  recipient: string;
  vault: boolean;
  nonce: number;
  deadline: number;
  signature: string;
}

MPC Service (mpc/src/teleporter.ts)

Secure MPC signing with:

  1. spawn() instead of exec() - No shell injection
  2. Strict hex validation: /^[0-9a-f]{64}$/i
  3. Bounded retries: Exponential backoff, max 3 attempts
  4. Process management: Timeout and cleanup
  5. Graceful shutdown: SIGTERM handling

Pluggable threshold-signer bus (mpc/src/signers/)

MPC_SCHEME env var selects the signer; default ecdsa is byte-identical to the legacy GG18 path. Each scheme exposes policyId() for the StrictPQProfileGate to consult.

MPC_SCHEME policyId Implementation
ecdsa ECDSA-SECP256K1-GG18 spawn(gg18_sign_client) — existing flow
cggmp21 ECDSA-SECP256K1-CGGMP21 Stub — TODO pick @pier-wallet/mpc-ecdsa-wasm or @leo-003/cggmp-wasm
frost SCHNORR-SECP256K1-FROST-RFC9591 @noble/curves secp256k1_FROST (real)
pulsar PULSAR-M-LWE-FIPS204T JSON-RPC client → luxfi/pulsar daemon
corona CORONA-R-LWE JSON-RPC client → luxfi/corona daemon
double-lattice DOUBLE-LATTICE-PULSAR+CORONA Composition: u32_be(len)‖pulsar‖corona

Adding a scheme = one file in mpc/src/signers/ + one case in the registry (signers/index.ts). All schemes implement the same ThresholdSigner interface (sign, verify, keygenInit/Round/Finish).

Daemon RPC surface required for pulsar / corona:

pulsar.keygen { threshold, participants } -> { publicKey, shares: [hex] }
pulsar.sign   { messageHex, pubKeyHex }   -> { signatureHex }
pulsar.verify { messageHex, signatureHex, pubKeyHex } -> { ok }

(same shape under the corona.* namespace). Neither daemon currently exposes this surface — pulsar/ref/go/cmd/genkat and corona/cmd/*_oracle/ are vector generators, not long-running services. Follow-up: ship pulsard / coronad with the above JSON-RPC methods.

Configuration

api/settings.json

{
  "RPC": { "<chainId>": "<rpc_url>" },
  "Bridges": { "<chainName>": "<address>" },
  "AllowedTokens": { "<token>": { "<chain>": "<address>" } },
  "NetNames": { "<chainId>": "<chainName>" },
  "DB": "<mongodb_password>",
  "SigningManagers": { "0": "<url>" },
  "KeyStore": { "0": "<filename>" },
  "MaxRetries": 3,
  "RetryDelayMs": 2000
}

mpc/settings.json

{
  "RPC": { "<chainId>": "<rpc_url>" },
  "Teleporter": { "<chainName>": "<address>" },
  "NetNames": { "<chainId>": "<chainName>" },
  "DB": "<mongodb_password>",
  "SigningManagers": { "0": "<url>" },
  "MPCPeers": { "0": "<host>, <ip>" },
  "KeyStore": { "0": "<filename>" },
  "SMTimeout": "1"
}

Development

Prerequisites

  • Node.js 18+
  • MongoDB
  • Hardhat (for contracts)

Build

# Contracts
cd contracts && pnpm install && pnpm build

# API
cd api && pnpm install && pnpm build

# MPC
cd mpc && pnpm install && pnpm build

Test

# Contracts
cd contracts && pnpm test

# API
cd api && pnpm test

# MPC
cd mpc && pnpm test

Security Checklist

Contract Security

  • EIP-712 typed data signatures
  • ClaimId-based replay protection
  • Token whitelisting
  • Role separation (ADMIN, ORACLE, PAUSER)
  • ReentrancyGuard on external functions
  • Pausable for emergencies
  • SafeERC20 for token transfers
  • Fee rate caps (max 10%)
  • Zero address validation

API/MPC Security

  • POST endpoints for sensitive operations
  • Derive all claim fields from chain logs
  • Strict hex validation
  • spawn() instead of exec()
  • Rate limiting
  • Helmet security headers
  • Bounded retries with backoff
  • Graceful shutdown

Test Results

All 36 tests passing:

  Lux Teleport Bridge
    Deployment
      ✔ Should set the correct admin
      ✔ Should initialize with correct domain separator
    Token Allowlist
      ✔ Should allow admin to add tokens
      ✔ Should prevent non-admin from adding tokens
    Bridge Burn
      ✔ Should burn tokens and emit event with destination data
      ✔ Should reject burn for non-allowed tokens
      ✔ Should reject burn with zero amount
    Bridge Mint with EIP-712 Signatures
      ✔ Should mint tokens with valid oracle signature
      ✔ Should reject replay of same claim
      ✔ Should reject expired claims
      ✔ Should reject invalid oracle signature
    Pausability
      ✔ Should allow pauser to pause
      ✔ Should reject burns when paused
    Oracle Management
      ✔ Should allow admin to activate/deactivate oracles
    Fee Configuration
      ✔ Should allow admin to update fees
      ✔ Should reject fee rate above maximum
      ✔ Should correctly deduct fees on mint
    Adversarial Tests
      ✔ Should reject burn to same chain
      ✔ Should reject burn to chain 0
      ✔ Should reject burn to zero recipient
      ✔ Should reject mint to zero recipient
      ✔ Should reject mint when paused
      ✔ Should reject signature from deactivated oracle
      ✔ Should reject emergency withdraw from non-admin
      ✔ Should reject setting zero address as oracle
      ✔ Should reject mint with zero amount
      ✔ Should reject mint for non-allowed token
      ✔ Should handle signature with manipulated s-value (malleability)

  ERC20B (Bridgeable Token)
    Deployment
      ✔ Should set correct name and symbol
      ✔ Should grant admin role to deployer
    Bridge Operations
      ✔ Should allow bridge to mint
      ✔ Should allow bridge to burn
      ✔ Should reject mint from non-bridge
      ✔ Should reject burn from non-bridge
    Pausability
      ✔ Should allow admin to pause
      ✔ Should reject transfers when paused

  36 passing (546ms)

Migration from v1

  1. Deploy new Bridge.sol with EIP-712 support
  2. Configure token whitelist
  3. Add oracle addresses with ORACLE_ROLE
  4. Update API to POST endpoints (/api/signature)
  5. Update frontend to call bridgeBurn with destination params
  6. Update claim flow to use new signature format

EVM Precompiles for ZK Operations

BN254/alt_bn128 (EIP-1108) - Ready ✅

Essential for Groth16 SNARK verification. Lux EVM chains have these active from genesis:

Address Precompile Gas (EIP-1108) Status
0x06 ECADD 150 Active
0x07 ECMUL 6,000 Active
0x08 ECPAIRING 45,000 + 34,000/pair Active

Verification:

  • github.com/luxfi/geth/core/vm/contracts.go contains full implementation
  • params.Bn256AddGasIstanbul = 150 (EIP-1108 repricing)
  • Fork config: berlinBlock: 0 means EIP-1108 pricing is active
  • Both evm and coreth import from github.com/luxfi/geth/core/vm

BLS12-381 (EIP-2537) - Ready for Prague ✅

For BLS signature verification and future Quasar consensus proofs:

Address Precompile Gas
0x0b G1Add 375
0x0c G1MultiExp 12,000
0x0d G2Add 600
0x0e G2MultiExp 22,500
0x0f Pairing 37,700 + 32,600/pair
0x10 MapG1 5,500
0x11 MapG2 23,800

Available in PrecompiledContractsPrague. Enable by setting pragueTime in chain config.

Usage for Trustless Bridge

The BN254 / BLS12-381 EVM precompiles are kept available so Lux EVM chains can verify classical proofs and BLS signatures cheaply when bridging to an external classical L1 EVM. Under LUX_STRICT_E2E_PQ these precompiles are NOT used in the Z-Chain proof slot — that slot is gated to ProofPolicySTARKFRISHA3PQ (STARK / FRI / SHA-3) and classical pairings / BLS aggregates are refused at the profile gate. The examples below describe the classical-compat path.

Z-Chain Groth16 Verification (classical-compat bridge to L1 EVM only; NOT canonical under strict-PQ):

// Verify Groth16 proof using BN254 pairing
function verifyProof(
    uint256[2] memory a,
    uint256[2][2] memory b,
    uint256[2] memory c,
    uint256[] memory input
) public view returns (bool) {
    // Uses 0x08 ECPAIRING precompile
    // Gas: 45,000 + 34,000 * numPairs
}

Quasar Consensus Verification (classical-compat profile only; refused under strict-PQ):

// Verify BLS aggregate signature using BLS12-381
function verifyQuasarSignature(
    bytes memory signature,
    bytes32 message,
    bytes[] memory publicKeys
) public view returns (bool) {
    // Uses 0x0f BLS12-381 pairing precompile
    // Requires Prague upgrade
    // NOT canonical under LUX_STRICT_E2E_PQ — strict-PQ chains use
    // Pulsar / Pulsar-M threshold signatures (luxfi/pulsar,
    // luxfi/pulsar-m) instead of BLS aggregates for finality.
}

Dependencies

Contracts use latest stable versions:

  • Hardhat 2.22.19
  • OpenZeppelin Contracts 5.2.0
  • ethers 6.13.5
  • TypeScript 5.7.3

Privacy layer — removed

The privacy layer that previously sat at the bottom of this file (PrivateBridge, ZNote, ZChainAMM, BulletproofVerifier and PrivateTeleport) was removed in commit 49522d3 ("chore: remove deprecated privacy contracts"). The contracts no longer compile in this repo, the tests for them no longer exist (Privacy.test.ts / PrivateTeleport.test.ts were deleted in the Tier A pass).

The historical content is preserved in git history; nothing in the current codebase composes against it. See CHANGELOG.md for the removal rationale.

For private cross-chain transfers, the canonical surface today is the Warp 2.0 envelope path (BridgeV2.sol + WARP2_BRIDGE.md) plus the M-Chain custody layer (LP-7330). Privacy is delegated to per-chain shielded pools at the application layer, not built into the bridge.

Submission documentation

The Tier A submission package lives in this repo:

  • SUBMISSION.md — cover sheet.
  • SPEC.md — bridge state machine.
  • PROOF-CLAIMS.md — what is proved, what is not.
  • TRUSTED-COMPUTING-BASE.md — TCB inventory.
  • PATENTS.md — royalty-free grant.
  • CRYPTOGRAPHER-SIGN-OFF.md — independent review.
  • DEPLOYMENT-RUNBOOK.md — operator-facing playbook.
  • LEGACY-CLASSICAL.md — opt-in classical-compat profile.
  • CHANGELOG.md — per-release changes.
  • WARP2_BRIDGE.md — Warp 2.0 architecture and Phase 1-5 migration.