Files
zeekay 75be0e607e round: rename Helper to Base — name the value, not the laziness
round.Helper was the canonical anti-pattern: a bare-noun
qualifier-name that told you nothing about what the type does. The
type is the embeddable Session implementation that protocol round
structs anchor on — its VALUE is "the base every round builds on".
Rename it to that.

  type Helper struct { ... }   → type Base struct { ... }
  func (h *Helper) ...         → func (h *Base) ...
  func NewSession(...) (*Helper, error) → (*Base, error)
  Output { *Helper }           → Output { *Base }
  Abort  { *Helper }           → Abort  { *Base }
  round.Helper                 → round.Base   (18 files)
  Helper: helper               → Base: helper (struct literal init)
  r.Helper.X / r.Helper        → r.Base.X / r.Base (field access)

internal/round/helper.go    → base.go
internal/round/helper_test.go → base_test.go

Forward-only — no type alias, no deprecation. The Go FAQ pointer-
receiver consistency landed in the prior commit (f7b1365) so the
method set is already aligned; this commit only renames the type.

Build + vet clean; round / CMP keygen smoke / FROST keygen all
PASS unchanged.
2026-06-01 00:26:33 -07:00

136 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package keygen
import (
"crypto/rand"
"errors"
"github.com/luxfi/threshold/internal/round"
"github.com/luxfi/threshold/internal/types"
"github.com/luxfi/threshold/pkg/math/curve"
"github.com/luxfi/threshold/pkg/math/polynomial"
"github.com/luxfi/threshold/pkg/math/sample"
"github.com/luxfi/threshold/pkg/paillier"
"github.com/luxfi/threshold/pkg/party"
"github.com/luxfi/threshold/pkg/protocol"
zksch "github.com/luxfi/threshold/pkg/zk/sch"
)
var _ round.Round = (*round1)(nil)
type round1 struct {
*round.Base
// PreviousSecretECDSA = sk'ᵢ
// Contains the previous secret ECDSA key share which is being refreshed
// Keygen: sk'ᵢ = nil
// Refresh: sk'ᵢ = sk'ᵢ
PreviousSecretECDSA curve.Scalar
// PreviousPublicSharesECDSA[j] = pk'ⱼ
// Keygen: pk'ⱼ = nil
// Refresh: pk'ⱼ = pk'ⱼ
PreviousPublicSharesECDSA map[party.ID]curve.Point
// PreviousChainKey contains the chain key, if we're refreshing
//
// In that case, we will simply use the previous chain key at the very end.
PreviousChainKey types.RID
// VSSSecret = fᵢ(X)
// Polynomial from which the new secret shares are computed.
// Keygen: fᵢ(0) = xⁱ
// Refresh: fᵢ(0) = 0
VSSSecret *polynomial.Polynomial
// keyID is the stable cross-phase identifier for this key generation context
keyID *protocol.KeyID
}
// VerifyMessage implements round.Round.
func (r *round1) VerifyMessage(round.Message) error { return nil }
// StoreMessage implements round.Round.
func (r *round1) StoreMessage(round.Message) error { return nil }
// Finalize implements round.Round
//
// - sample Paillier (pᵢ, qᵢ)
// - sample Pedersen Nᵢ, sᵢ, tᵢ
// - sample aᵢ <- 𝔽
// - set Aᵢ = aᵢ⋅G
// - compute Fᵢ(X) = fᵢ(X)⋅G
// - sample ridᵢ <- {0,1}ᵏ
// - sample cᵢ <- {0,1}ᵏ
// - commit to message.
func (r *round1) Finalize(out chan<- *round.Message) (round.Session, error) {
// generate Paillier and Pedersen
PaillierSecret := paillier.NewSecretKey(r.Pool)
SelfPaillierPublic := PaillierSecret.PublicKey
SelfPedersenPublic, PedersenSecret := PaillierSecret.GeneratePedersen()
ElGamalSecret, ElGamalPublic := sample.ScalarPointPair(rand.Reader, r.Group())
// save our own share already so we are consistent with what we receive from others
SelfShare := r.VSSSecret.Evaluate(r.SelfID().Scalar(r.Group()))
// set Fᵢ(X) = fᵢ(X)•G
SelfVSSPolynomial := polynomial.NewPolynomialExponent(r.VSSSecret)
// generate Schnorr randomness
SchnorrRand := zksch.NewRandomness(rand.Reader, r.Group(), nil)
// Sample RIDᵢ
SelfRID, err := types.NewRID(rand.Reader)
if err != nil {
return r, errors.New("failed to sample Rho")
}
chainKey, err := types.NewRID(rand.Reader)
if err != nil {
return r, errors.New("failed to sample c")
}
// commit to data in message 2
// Use KeyID for stable cross-phase commitments instead of sessionID-based hash
SelfCommitment, Decommitment, err := r.keyID.HashForParty(r.SelfID()).Commit(
SelfRID, chainKey, SelfVSSPolynomial, SchnorrRand.Commitment(), ElGamalPublic,
SelfPedersenPublic.N(), SelfPedersenPublic.S(), SelfPedersenPublic.T())
if err != nil {
return r, errors.New("failed to commit")
}
// should be broadcast but we don't need that here
msg := &broadcast2{Commitment: SelfCommitment}
err = r.BroadcastMessage(out, msg)
if err != nil {
return r, err
}
nextRound := &round2{
round1: r,
ElGamalSecret: ElGamalSecret,
PaillierSecret: PaillierSecret,
PedersenSecret: PedersenSecret,
SchnorrRand: SchnorrRand,
Decommitment: Decommitment,
}
// Initialize sync.Maps with self values
nextRound.VSSPolynomials.Store(r.SelfID(), SelfVSSPolynomial)
nextRound.Commitments.Store(r.SelfID(), SelfCommitment)
nextRound.RIDs.Store(r.SelfID(), SelfRID)
nextRound.ChainKeys.Store(r.SelfID(), chainKey)
nextRound.ShareReceived.Store(r.SelfID(), SelfShare)
nextRound.ElGamalPublic.Store(r.SelfID(), ElGamalPublic)
nextRound.PaillierPublic.Store(r.SelfID(), SelfPaillierPublic)
nextRound.Pedersen.Store(r.SelfID(), SelfPedersenPublic)
return nextRound, nil
}
// PreviousRound implements round.Round.
func (round1) PreviousRound() round.Round { return nil }
// MessageContent implements round.Round.
func (*round1) MessageContent() round.Content { return nil }
// Number implements round.Round.
func (*round1) Number() round.Number { return 1 }