mirror of
https://github.com/luxfi/lens.git
synced 2026-10-30 16:21:16 +00:00
Lens is the curve-based threshold signing kernel for Lux. It is the classical analogue of Pulsar (the lattice kernel at github.com/luxfi/pulsar). Both kernels share the same lifecycle shape — Bootstrap → Reshare* → Reanchor — and the same activation circuit-breaker contract. They differ only in the math field: Pulsar over R_q (Module-LWE), Lens over a prime-order curve group (discrete log). Curves shipped: Ed25519, secp256k1, Ristretto255. Each has a deterministically-derived secondary Pedersen generator H from a Lens-specific nothing-up-my-sleeve tag. Sign math: FROST 2-round Schnorr threshold signing per RFC 9591 (Komlo-Goldberg 2020). Verifier checks z·G ?= R + c·X under the unchanged group public key. Reshare math: HJKY97 zero-poly Refresh + Desmedt-Jajodia ReshareToNewSet over the curve scalar field. Master secret s is preserved; only the share distribution rotates per epoch. Activation cert message format: QUASAR-LENS-ACTIVATE-v1, byte-equivalent shape to QUASAR-PULSAR-ACTIVATE-v1. Hash suite: HashSuite interface with Lens-SHA3 (production, cSHAKE256 / KMAC256 / TupleHash256) and Lens-BLAKE3 (legacy) profiles. DKG: distributed FROST DKG with Feldman commitments. No trusted dealer required at genesis if the chain prefers proper distributed DKG. KAT oracles: cmd/reshare_oracle and cmd/dkg_oracle emit deterministic JSON vectors for cross-language byte-equality validation against a future luxcpp/crypto/lens/ port. Tests: full unit + integration coverage on every shipped curve. 60 unique tests, 0 failures.
83 lines
2.7 KiB
Go
83 lines
2.7 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package reshare
|
|
|
|
// Pedersen polynomial commitments for VSR over the curve.
|
|
//
|
|
// The Reshare kernel in reshare.go gives the arithmetic core of
|
|
// Desmedt-Jajodia '97. To make Reshare verifiable in a permissionless
|
|
// setting we also commit each old party i's resharing polynomial
|
|
// g_i(X) and let the new committee verify the values g_i(β_j) it
|
|
// receives against the commitment. The same commitment scheme covers
|
|
// Refresh's z_i(X).
|
|
//
|
|
// Public commitment to f_i(X) = c_{i,0} + c_{i,1}·X + ... + c_{i,t-1}·X^{t-1}:
|
|
//
|
|
// C_{i,k} = c_{i,k}·G + r_{i,k}·H
|
|
//
|
|
// where G is the curve's base point and H is the curve's secondary
|
|
// generator (lens.<curve>.h-base.v1 nothing-up-my-sleeve point).
|
|
//
|
|
// The recipient at evaluation point x verifies its (share, blind) pair
|
|
// against the commitment vector via:
|
|
//
|
|
// share·G + blind·H ?= Σ_{k=0..t-1} x^k · C_{i,k}
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/luxfi/lens/hash"
|
|
"github.com/luxfi/lens/primitives"
|
|
)
|
|
|
|
// Errors specific to commitment verification.
|
|
var (
|
|
ErrCommitMismatch = errors.New("reshare: commitment verification failed")
|
|
ErrCommitWrongLength = errors.New("reshare: commit vector has wrong length")
|
|
ErrInconsistentDigests = errors.New("reshare: cross-recipient commit digest mismatch")
|
|
)
|
|
|
|
// CommitToPoly produces the t Pedersen commitments to the secret-poly
|
|
// coefficients c_k together with the matching blinding-poly
|
|
// coefficients r_k.
|
|
func CommitToPoly(
|
|
c primitives.Curve,
|
|
secretCoeffs, blindCoeffs []primitives.Scalar,
|
|
) ([]primitives.Point, error) {
|
|
return primitives.PedersenVectorCommit(c, secretCoeffs, blindCoeffs)
|
|
}
|
|
|
|
// VerifyShareAgainstCommits checks the recipient-side equation
|
|
//
|
|
// share·G + blind·H ?= Σ_{k=0..t-1} x^k · C_k
|
|
func VerifyShareAgainstCommits(
|
|
c primitives.Curve,
|
|
share, blind primitives.Scalar,
|
|
commits []primitives.Point,
|
|
x primitives.Scalar,
|
|
) error {
|
|
return primitives.VerifyPedersenShare(c, share, blind, commits, x)
|
|
}
|
|
|
|
// CommitDigest returns the canonical 32-byte digest over a commit
|
|
// vector under the supplied HashSuite. suite=nil resolves to the
|
|
// production default (Lens-SHA3).
|
|
func CommitDigest(commits []primitives.Point, suite hash.HashSuite) [32]byte {
|
|
s := hash.Resolve(suite)
|
|
parts := make([][]byte, 0, 1+len(commits))
|
|
parts = append(parts, []byte("lens.reshare.commit-digest.v1"))
|
|
for _, p := range commits {
|
|
var buf bytes.Buffer
|
|
bytes, _ := p.MarshalBinary()
|
|
var lenBuf [4]byte
|
|
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(bytes)))
|
|
buf.Write(lenBuf[:])
|
|
buf.Write(bytes)
|
|
parts = append(parts, buf.Bytes())
|
|
}
|
|
return s.TranscriptHash(parts...)
|
|
}
|