mirror of
https://github.com/luxfi/bridge.git
synced 2026-09-24 00:26:46 +00:00
Adds the missing primitive the bridge needs to opt swaps into a PQ-safe
threshold protocol (pulsar / corona / magnetar) instead of always going
through the daemon's classical defaults (cggmp21 / frost).
This PR is data-model only — establishes the vocabulary and the helpers
(IsPostQuantum, CompatibleCurve, ProtocolFor). The wire-plumbing
through mchain.Client.signViaDashboard + signing_driver gating off
bridge.BridgeProfile is the next focused PR; it touches the dashboard
call path consistently across auth.go and client.go and so wants its
own review.
Protocol catalogue mirrors mpcd's dispatch matrix
(luxfi/mpc/pkg/api/handlers_kms.go + luxfi/threshold/protocols/*):
classical:
cggmp21 secp256k1, default ECDSA
frost ed25519 / sr25519, threshold Schnorr/EdDSA
bls BLS12-381 aggregate (classical Beam in Warp 2.0)
doerner 2-of-2 OT-based ECDSA
post-quantum:
pulsar-m-65 Module-LWE NIST L1 (~128-bit classical)
pulsar-m-87 Module-LWE NIST L3 (~192-bit classical)
corona Ring-LWE (rank-1 MLWE collapse — production lane on
primary network, Warp 2.0 Pulse)
magnetar SLH-DSA / FIPS 205 hash-based (per-validator only —
true threshold is research-grade per Bonte-Smart-Tan
2023)
ProtocolDefault ("") is the wire sentinel for "let the daemon infer
from curve" — preserves backward compatibility with every current
caller. The empty-string contract is locked in a regression test.
Tests pass: ./internal/mchain (4.0s) + full repo green.
This is the foundation for the v1.2.0 PQ posture. The bridge's profile
gate (top-level profile.go) already names pulsar-m-65 / pulsar-m-87 /
ml-dsa-65 / ml-dsa-87 as the strict-PQ schemes; the Protocol enum is
the seam between that profile and the mpcd request body.
134 lines
5.2 KiB
Go
134 lines
5.2 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package mchain
|
|
|
|
// Protocol names the threshold-signature PROTOCOL the MPC cluster runs.
|
|
//
|
|
// Curve answers "what mathematical structure does the signature live in?"
|
|
// (secp256k1, ed25519). Protocol answers "what threshold sign ceremony
|
|
// produces it?" (cggmp21, frost, pulsar, corona, …). The two are
|
|
// orthogonal — pulsar produces secp256k1 signatures, frost produces
|
|
// ed25519 signatures, and a single curve admits several protocols.
|
|
//
|
|
// Lux protocol matrix as exposed by mpcd (luxfi/mpc/cmd/mpcd):
|
|
//
|
|
// classical, ECDSA ─ cggmp21 (default for secp256k1)
|
|
// classical, EdDSA ─ frost (default for ed25519, sr25519)
|
|
// classical, pairing ─ bls (BLS12-381 aggregate)
|
|
// 2-of-2 ECDSA ─ doerner (OT-based)
|
|
// post-quantum, MLWE ─ pulsar-m-65 / pulsar-m-87
|
|
// post-quantum, RLWE ─ corona
|
|
// post-quantum, hash-based ─ magnetar (per-validator standalone)
|
|
//
|
|
// Empty Protocol means "let the daemon choose based on curve" — the
|
|
// pre-Protocol behaviour, preserved for backward compatibility. Existing
|
|
// SignForWalletOnCurve call paths continue to work unchanged.
|
|
//
|
|
// Wire format: the string value is the field the daemon expects. mpcd
|
|
// accepts these literally in the request body's `protocol` field — see
|
|
// pkg/api/handlers.go for the dispatch table.
|
|
type Protocol string
|
|
|
|
const (
|
|
// ProtocolDefault means "infer from curve" — equivalent to omitting
|
|
// the field. ECDSA → cggmp21, EdDSA → frost.
|
|
ProtocolDefault Protocol = ""
|
|
|
|
// ProtocolCGGMP21 is the canonical threshold-ECDSA protocol from
|
|
// Canetti-Gennaro-Makriyannis-Peled 2021. secp256k1 + variants.
|
|
ProtocolCGGMP21 Protocol = "cggmp21"
|
|
|
|
// ProtocolFROST is the canonical threshold-Schnorr/EdDSA protocol.
|
|
// Drives ed25519 (SOL, TON), sr25519 (DOT).
|
|
ProtocolFROST Protocol = "frost"
|
|
|
|
// ProtocolBLS is BLS12-381 aggregate-signature threshold. Used for
|
|
// the classical Beam lane in Warp 2.0 envelopes.
|
|
ProtocolBLS Protocol = "bls"
|
|
|
|
// ProtocolDoerner is the 2-of-2 OT-based ECDSA from Doerner-Kondi-
|
|
// Lee-shelat. Niche threshold = exactly 2.
|
|
ProtocolDoerner Protocol = "doerner"
|
|
|
|
// ProtocolPulsarM65 is the Module-LWE threshold signature at NIST
|
|
// Category 1 (M-65 parameter set, ≈ 128-bit classical security).
|
|
// PQ-safe, leaderless, permissionless-safe by design. Default PQ
|
|
// lane for general bridge swaps.
|
|
ProtocolPulsarM65 Protocol = "pulsar-m-65"
|
|
|
|
// ProtocolPulsarM87 is Pulsar at NIST Category 3 (M-87 parameter
|
|
// set, ≈ 192-bit classical security). Reserved for higher-value
|
|
// strict-PQ flows.
|
|
ProtocolPulsarM87 Protocol = "pulsar-m-87"
|
|
|
|
// ProtocolCorona is the Ring-LWE specialisation of Pulsar (rank-1
|
|
// MLWE collapses to RLWE). Production R-LWE lane on Lux primary
|
|
// network — wired into Warp 2.0 Pulse, see luxfi/warp/pulsar.
|
|
// PQ-safe.
|
|
ProtocolCorona Protocol = "corona"
|
|
|
|
// ProtocolMagnetar is FIPS 205 SLH-DSA, hash-based PQ signature.
|
|
// Per-validator standalone primitive — NOT true threshold without
|
|
// trusted dealer (Bonte-Smart-Tan 2023). Reserved for public-BFT
|
|
// consensus aggregate certificate (QuasarCert).
|
|
ProtocolMagnetar Protocol = "magnetar"
|
|
)
|
|
|
|
// IsPostQuantum reports whether p is PQ-safe.
|
|
//
|
|
// True for: pulsar-m-65, pulsar-m-87, corona, magnetar.
|
|
// False for: cggmp21, frost, bls, doerner. ProtocolDefault returns
|
|
// false — the inferred default is always classical.
|
|
func (p Protocol) IsPostQuantum() bool {
|
|
switch p {
|
|
case ProtocolPulsarM65, ProtocolPulsarM87, ProtocolCorona, ProtocolMagnetar:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// CompatibleCurve returns the Curve a given Protocol produces signatures
|
|
// on. The mapping is one-to-one for every protocol the bridge cares about:
|
|
//
|
|
// cggmp21 / pulsar-m-65 / pulsar-m-87 / corona / doerner → secp256k1
|
|
// frost → ed25519
|
|
// bls → bls12-381 (out-of-band; Curve enum does not list it)
|
|
// magnetar → slh-dsa (out-of-band)
|
|
//
|
|
// Returns ProtocolDefault → CurveSecp256k1 as the conservative default.
|
|
// BLS and Magnetar are intentionally NOT routed through the Curve enum —
|
|
// the bridge uses Curve for ECDSA/EdDSA family selection only; BLS and
|
|
// hash-based signatures take separate code paths.
|
|
func (p Protocol) CompatibleCurve() Curve {
|
|
switch p {
|
|
case ProtocolFROST:
|
|
return CurveEd25519
|
|
default:
|
|
return CurveSecp256k1
|
|
}
|
|
}
|
|
|
|
// ProtocolFor returns the canonical protocol for a given curve under the
|
|
// classical (non-PQ) default posture. PQ profiles override via the
|
|
// SignForWalletOnProtocol path — the bridge daemon's profile gate
|
|
// decides whether to pass a PQ protocol hint at all.
|
|
//
|
|
// secp256k1 → cggmp21
|
|
// ed25519 → frost
|
|
//
|
|
// Returns ProtocolDefault for unknown curves so the daemon's own default
|
|
// dispatch wins; the bridge MUST NOT silently downgrade to ECDSA when
|
|
// faced with an unknown curve.
|
|
func ProtocolFor(c Curve) Protocol {
|
|
switch c {
|
|
case CurveSecp256k1:
|
|
return ProtocolCGGMP21
|
|
case CurveEd25519:
|
|
return ProtocolFROST
|
|
default:
|
|
return ProtocolDefault
|
|
}
|
|
}
|