LPsLux Proposals
C-Chain
LP-2106

Threshold Lamport Protocol

Review

2-round threshold Lamport OTS over a canonical on-chain intent (no trusted assembler)

Category
Core
Created
2024-12-26

Abstract

This LP specifies a public, coordinatorless, 2-round threshold protocol that outputs a standard Lamport one-time signature verifiable by LP-4105. Signers never deliver plaintext shares to a trusted "MPC box". Instead, they post commitments and encrypted contributions to the LUX chain (bulletin board). After ≥t participants contribute for a session, the chain finalizes a Lamport signature for a canonical on-chain WithdrawalIntentHash, which can be relayed and verified on an EVM chain with exactly one Lamport verification.

Key innovation: This protocol solves ALL known threshold Lamport attacks from the EthResearch 2022 discussion and Vitalik's proposals:

  • Split-view/equivocation: Canonical on-chain WithdrawalIntentHash from finalized state
  • Bit harvesting: Single-use leafIndex with nextPKH rotation enforced on-chain
  • Trusted assembler: Encrypted contributions + T-Chain 69-of-100 threshold decryption
  • Synchronicity dependence: 2-round chain-based protocol (no N² gossip)
  • Missing accountability: Slash-on-failure with forced plaintext reveal

Important correction: the Lamport signature is a set of preimages. Those preimages necessarily become public at finalization. The security goal is that no party learns both branches (0/1 secrets) for a leaf unless ≥t parties collude, and that signers cannot be tricked into signing different messages for the same session.


Motivation

LP-4105 defines Lamport OTS for Lux Safe but assumes a single signer. Production deployments require threshold control where:

  1. T-of-N parties must cooperate
  2. No trusted assembler collects plaintext shares
  3. Anti-equivocation: a malicious leader/block producer cannot show different messages to different signers
  4. On-chain coordination: chain acts as bulletin board and enforces session rules
  5. Post-quantum authorization on destination chains (hash-based)

Specification

The threshold Lamport protocol consists of:

  1. Setup phase for share distribution
  2. Two-round signing protocol via on-chain coordination
  3. Threshold decryption for signature finalization
  4. Accountability mechanism for dispute resolution

Details follow in subsequent sections.

Threat Model and Security Goals

Assume an adversary can:

  • control/DoS some signers,
  • attempt equivocation (show different messages to different signers),
  • attempt to learn enough information to forge signatures for new messages.

Goals

G1 (Anti-equivocation): the only signable digest is derived from finalized on-chain state, so all honest signers necessarily bind to the same message.

G2 (Threshold unforgeability for a leaf): with < t corrupt signers, the adversary cannot compute a valid Lamport signature for any message other than the one finalized by the protocol for that leaf.

G3 (No trusted assembler): no single party receives all plaintext contributions; only the final Lamport signature is revealed publicly.

G4 (Accountability): parties that commit but fail to contribute (or contribute garbage) can be identified and penalized.


Definitions and Notation

SymbolDescription
ncommittee size (epoch validator set or configured signer set)
tthreshold required to finalize
iLamport bit index (0..255)
bbit value (0 or 1)
jsigner index / validator
r[j][i][b]signer j's secret share for bit i, branch b (32 bytes)
S[i][b]leaf secret preimage for bit i, branch b
PK[i][b]Lamport public key element = H(S[i][b])
H()keccak256 (or another LP-4105-approved hash)
leafIndexLamport leaf number (single-use)
epochLUX epoch that defines committee membership
PKHhash commitment to the Lamport public key (H(PK[...]))

Canonical Message to Sign (required)

To prevent "different message per signer" attacks, the signable digest MUST be derived from a finalized on-chain withdrawal intent:

WithdrawalIntentHash = keccak256(abi.encode(
    bytes32("LUX-WITHDRAW-V1"),
    srcChainId,
    dstChainId,
    burnTxHash,
    burnLogIndex,
    token,
    amount,
    recipient,
    nonce,
    epoch,
    leafIndex,
    nextPKH
));
  • epoch, leafIndex, nextPKH are enforced by the LUX contract.
  • No signer signs an off-chain "txHash" or leader-proposed blob.

Setup Phase (per leaf)

Each Lamport leaf has secrets:

S[i][b] = Σ_j r[j][i][b]  mod 2^256
PK[i][b] = H(S[i][b])

Each signer j samples r[j][i][b] ← random(32 bytes) for all i,b and stores only their own shares.

Publishing PKH (required)

The system MUST publish a commitment to the Lamport public key (or a Merkle/XMSS root committing to many leaves). Two acceptable approaches:

Option A (practical, small committees): MPC ceremony among signers to compute PK[i][b] (or PKH) without revealing S[i][b].

Option B (chain-coordinated): signers post commitments to their shares and the protocol includes an audit/dispute mechanism; publication may reveal some data if disputes occur.

This LP standardizes signing/finalization; key-generation ceremony details may be specified in an accompanying implementation note.


Protocol (2 rounds on LUX chain)

The protocol is implemented by a LUX contract (the "Coordinator" in the on-chain sense, not a trusted party). Any account can relay transactions; the contract enforces correctness.

Session Creation

A session is created for a specific WithdrawalIntentHash:

sessionId = keccak256(abi.encode(
    bytes32("LUX-LAMPORT-SESSION-V1"),
    WithdrawalIntentHash
));

The contract verifies the referenced withdrawal intent exists and is finalized.

Round 1 — Commit (bind signer + bind session)

Each signer j submits:

commit_j = H(
    bytes32("LUX-LAMPORT-COMMIT-V1") ||
    sessionId ||
    epoch || leafIndex ||
    WithdrawalIntentHash ||
    sharesRoot_j ||
    nonce_j
)

Where:

  • sharesRoot_j = H( r[j][*][*] for this leaf ) or a Merkle root over (i,b)→H(r[j][i][b])
  • nonce_j is signer-chosen randomness

Contract rules:

  • Accept commits only from the epoch committee.
  • Lock the participant set when ≥t commits are posted (or at a deadline).
  • Require a bond per signer to deter DoS.

Round 2 — Contribute (message-dependent, encrypted)

Let b_i = bit(WithdrawalIntentHash, i).

Each signer j posts ciphertexts:

ct_j[i] = ENC_to_committee( r[j][i][b_i], sessionId, i )

Requirements:

  • Ciphertexts MUST be addressed to a decryption committee corresponding to the epoch (e.g., threshold decryption key held by validators).
  • Plaintext shares MUST NOT be posted unless in a dispute.

Finalization — Threshold Reveal of the Lamport signature

When the contract has ≥t valid contributions for all 256 bits, the committee performs threshold decryption/reveal and publishes:

sig[i] = Σ_j r[j][i][b_i]  mod 2^256
Signature = sig[0..255]

T-Chain Integration (LP-7000, LP-7330)

The threshold decryption/reveal is performed by the T-Chain (Threshold Chain) validator set:

ParameterValue
Threshold69-of-100 validators
VMThresholdVM (LP-7330)
Decryption ProtocolCGGMP21 / Ringtail
ConsensusQuasar with threshold finality

Decryption flow:

  1. T-Chain validators receive encrypted contributions ct_j[i]
  2. Each validator holds a share of the epoch decryption key
  3. When 69+ validators provide decryption shares, the plaintext r[j][i][b_i] is reconstructed
  4. Aggregated signature sig[i] is computed and emitted on-chain

Implementation reference: github.com/luxfi/node/vms/thresholdvm

The contract then emits:

event SignatureFinalized(
    bytes32 indexed sessionId,
    bytes32 indexed WithdrawalIntentHash,
    uint256 epoch,
    uint256 leafIndex,
    bytes32 nextPKH,
    bytes32[256] sig,
    bytes32 signersBitmap
);

Lamport verification condition (LP-4105 compatible):

∀i: H(sig[i]) == PK[i][b_i]

Accountability (required)

Encrypted contributions can be garbage. The protocol MUST include an enforceable mechanism to punish non-cooperative or dishonest signers.

If finalization fails (missing bits / invalid signature / decryption failure), the contract opens a dispute window.

For any failing index i, a signer j can reveal:

  • plaintext r[j][i][b_i]
  • opening data proving it matches sharesRoot_j (Merkle branch or precommitted hash)
  • the nonce/opening that binds to commit_j

The contract verifies the reveal against commit_j material and:

  • slashes signers who did not contribute, or
  • slashes signers whose ciphertext cannot correspond to a valid committed share.

This is slow but implementable and enforceable.

B) ZK proof of well-formed encryption (optional)

Each ct_j[i] includes a proof that it encrypts a 32-byte value consistent with sharesRoot_j. This reduces disputes but increases complexity.


Key Rotation (required)

Lamport leaves are one-time. Each finalized signature commits to nextPKH.

Rules:

  • leafIndex MUST be single-use and monotonically increasing.
  • On finalization, the LUX contract updates currentPKH = nextPKH atomically.
  • Destination chain enforces the same rotation when verifying a withdrawal.

Recommended: use an XMSS/Merkle root committing to many Lamport leaves so destination stores one root and each withdrawal carries a Merkle proof for the leaf.


Message Format for Safe / Bridge Use

This LP does not sign arbitrary Safe tx bytes. It signs the canonical on-chain intent hash (anti-equivocation).

If integrating with Safe, the Safe module's "action" MUST be embedded into the intent (e.g., recipient/module/amount fields), and the intent hash is what gets signed.


Rationale

  • "No node ever reconstructs a Lamport preimage" is false if interpreted literally: the final Lamport signature is itself a set of preimages. The correct claim is: no single party learns both branch secrets for the leaf, and no trusted party receives all plaintext inputs.
  • The canonical intent hash is the decisive fix for the classic "leader sends different messages to different signers" attack.
  • Large validator sets should treat encryption/decryption as epoch infrastructure; small signer sets can run the same protocol off-chain (still 2 rounds) without posting ciphertexts on-chain.

Backwards Compatibility

This protocol produces standard Lamport signatures as defined in LP-4105. The on-chain verifier is unchanged:

// Same verification as single-signer Lamport
LamportLib.verify(message, signature, publicKey)

Only the off-chain signing process differs.


Test Cases

Test 1: 2-of-3 Signing

Setup:
  - 3 signers generate independent shares
  - Public key computed via MPC

Signing:
  - Two signers commit
  - Two signers contribute
  - Signature finalized via threshold reveal

Verification:
  - Standard Lamport verify on EVM with LP-4105 verifier returns true

Test 2: DoS - Missing Contribution

Setup:
  - Signer commits but doesn't contribute

Expected:
  - Timeout triggers dispute window
  - Non-contributing signer slashed after timeout
  - Remaining signers can restart if  t

Test 3: Garbage Contribution

Setup:
  - Signer contributes malformed ciphertext

Expected:
  - Dispute reveal proves mismatch
  - Signer slashed
  - Session can restart with honest signers

Test 4: Equivocation Attempt

Setup:
  - Attacker attempts to show different messages to different signers

Expected:
  - Impossible: signers only sign WithdrawalIntentHash derived from finalized on-chain state
  - No off-chain message proposal accepted

Security Considerations

Preimage Revelation

The final Lamport signature is a set of preimages—these necessarily become public. The security goal is that:

  1. No party learns both branch secrets (0/1) for any leaf position unless ≥t parties collude
  2. Signers cannot be tricked into signing different messages for the same session

Complete Resolution of Vitalik/EthResearch Threshold Lamport Critiques

LP-4106 addresses all known attacks from the 2022 EthResearch discussion and Vitalik's "8-choice Lamport" proposal:

AttackDescriptionLP-4106 DefenseStatus
Split-View / EquivocationMalicious BP shows different messages to different signersCanonical on-chain WithdrawalIntentHash - signers derive message from finalized state only✅ SOLVED
Bit Harvesting Across SessionsAttacker learns one branch per bit across multiple sessionsleafIndex is single-use + monotonic; nextPKH enforced on-chain; cannot reuse leaves✅ SOLVED
8-Choice Slippage AttackVitalik's compression variant vulnerable to partial revelationWe use standard 256-bit Lamport (not 8-choice); each bit independent✅ N/A
Trusted AssemblerCentral party receives all plaintext sharesNo assembler; encrypted contributions + T-Chain threshold decryption (69-of-100)✅ SOLVED
Synchronicity DependenceRequires N² gossip for signers to agree on message2-round protocol via chain bulletin board; no signer-to-signer communication✅ SOLVED
Missing AccountabilityNo way to punish non-cooperative signersSlash-on-failure with forced plaintext reveal; bonds required✅ SOLVED
Replay / Double-SpendSame signature used for multiple withdrawals(epoch, leafIndex, nonce) tuple uniquely identifies session; contract enforces single-use✅ SOLVED

Defense Against Split-View / Equivocation Attack (EthResearch 2022)

The 2022 EthResearch critique of threshold Lamport describes a split-view / equivocation attack:

Each signer reveals only the portion of the Lamport material they're responsible for. A malicious block producer can craft different valid messages B_i and send a different one to each signer such that the particular query bits that signer will reveal match the attacker's target digest H'. The attacker aggregates the returned openings and ends up with a "valid-enough" signature for H' without any signer ever seeing H'.

Root cause: The scheme assumes "everyone is signing the same hash" but does not enforce it. Security depends on synchrony / signers gossiping the hash.

How LP-4106 Solves This Attack

LP-4106 fixes this class of attacks by moving message agreement from "signers talking to each other" to "signers talking to a single canonical bulletin board with finality":

1. The Signable Digest is NOT Leader-Proposed

Signers do not sign "a block / tx hash someone sent me". They sign exactly:

WithdrawalIntentHash = keccak256(abi.encode(... finalized on-chain fields ...))

The contract only allows sessions for intents that exist and are finalized. This means:

  • There is exactly one canonical message per intent, derived from chain state
  • A malicious relayer/BP cannot give signer A one message and signer B another while still calling it "the same session"
2. Sessions Bind Everyone to the Same Hash
sessionId = H("LUX-LAMPORT-SESSION-V1", WithdrawalIntentHash)

Round 1 commits include sessionId and WithdrawalIntentHash. Once the contract locks the participant set, "the thing being signed" is objectively fixed in public state.

The "send different blocks to different signers" move becomes irrelevant: signers are not signing an off-chain proposal; they are signing the chain-finalized intent hash.

3. The Chain is the Anti-Equivocation Communication Channel

The critique says: "solved if signers talk to each other to ensure they see the same hash".

LP-4106's answer: they don't need to talk to each other; they only need to:

  1. Read finalized state from the chain
  2. Post commits/contributions to the chain

The chain provides:

  • A single agreed transcript (bulletin board)
  • A single message (intent hash) enforced by consensus + contract rules

Attack Failure Analysis

Map the attacker's steps to this protocol:

Attack VectorLP-4106 Defense
Show signer i a different message off-chainSigner ignores it. The signer computes WithdrawalIntentHash from finalized on-chain intent fields (or directly from contract storage/events). The contract enforces that the session is tied to that hash. No "per-signer message" surface.
Create multiple different intents (one per signer)Then there are multiple different sessions with different sessionIds. Commit format binds commit_j to sessionId and WithdrawalIntentHash. Contributions are bound to sessionId. The attacker can't mix and match partial openings from different sessions to form a signature for a single target hash.
Reuse the same one-time leaf across sessionsleafIndex is single-use + monotonic and contract-enforced rotation via nextPKH. Prevents "harvest openings across multiple sessions until enough is learned".

The Coordination Guarantee

LP-4106 makes the scheme secure by changing the assumption from "signers are synchronous / gossip hashes" to:

Finality / common-view assumption for the Lux chain: Honest signers derive the signable digest only from finalized on-chain state.

This is the right place to put the assumption, because it's already required for any bridge/safe authorization. If your chain can be equivocating at "finalized state", everything breaks, not just signatures.

Practical spec rule: Commits are only valid after the intent is finalized by the Lux contract (not merely "seen in mempool" / "included in a block").

Why This Solves "Synchronicity Dependence"

The critique says: "the scheme depends on parties communicating with each other → interactive scheme".

LP-4106 accepts "interactive" but makes it 2 rounds via the chain, not N² gossip:

  1. Round 1: Public commit on-chain (binds signer to the unique canonical message)
  2. Round 2: Contribute on-chain (bound to that session/message)
  3. Finalization: Public outcome (Lamport signature or failure with accountability)

This interaction is:

  • Public (everyone sees the same thing)
  • Globally ordered (chain consensus)
  • Enforceable/slashable (bonds and dispute mechanisms)

That is precisely the missing piece in the EthResearch construction.

Threshold Security

With t-of-n threshold:

  • Any t parties can produce a valid signature
  • Fewer than t parties cannot forge a signature
  • Compromising t-1 parties reveals insufficient information for forgery

Anti-Equivocation

The canonical on-chain intent hash prevents the classic attack where a malicious leader shows different messages to different signers. All honest signers bind to the same finalized on-chain state.

Quantum Resistance

Security relies only on:

  1. Preimage resistance of keccak256
  2. Security of the threshold protocol

Both are believed quantum-resistant (keccak256 provides 128-bit security against Grover's algorithm).

Accountability and DoS Mitigation

  • Bonds required per signer to deter commit-but-don't-contribute attacks
  • Dispute mechanism allows identification and slashing of malicious signers
  • Session restart possible with different participant set

LPTitleRelationship
LP-4105Lamport One-Time Signatures for Lux SafeBase single-signer Lamport specification
LP-7000T-Chain Core Threshold SpecificationProvides threshold decryption infrastructure
LP-7330T-Chain ThresholdVM SpecificationVM implementation for threshold operations
LP-7321FROST Threshold Signature PrecompileAlternative threshold scheme (Schnorr)
LP-7322CGGMP21 Threshold ECDSA PrecompileECDSA threshold for bridge integration
LP-7324Ringtail Threshold Signature PrecompilePost-quantum threshold signatures
LP-3310Safe Multisig StandardIntegration with Lux Safe
LP-8100FHE Precompiles and InfrastructurePrivacy layer with 69-of-100 threshold

References

  1. EthResearch 2022: "Problems with Threshold Lamport" - Analysis of split-view/equivocation attacks on naive threshold Lamport constructions
  2. Lamport 1979: "Constructing Digital Signatures from a One-Way Function" - Original Lamport OTS paper
  3. LP-4105: "Lamport One-Time Signatures (OTS) for Lux Safe" - Single-signer Lamport specification
  4. Vitalik 2022: "8-choice Lamport" variant with slippage compression (also vulnerable to equivocation without chain coordination)
  5. LP-7000: T-Chain Core Threshold Specification - Threshold signature infrastructure
  6. LP-7330: T-Chain ThresholdVM Specification - VM for threshold operations