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

124 lines
3.2 KiB
Go

package keygen
import (
"errors"
"github.com/luxfi/threshold/internal/round"
"github.com/luxfi/threshold/internal/types"
"github.com/luxfi/threshold/pkg/hash"
"github.com/luxfi/threshold/pkg/math/curve"
"github.com/luxfi/threshold/pkg/party"
"github.com/luxfi/threshold/protocols/lss/config"
)
// round3 finalizes the keygen protocol
type round3 struct {
*round.Base
// Data from previous rounds
commitments map[party.ID]map[party.ID]curve.Point
chainKeys map[party.ID]types.RID
shares map[party.ID]curve.Scalar
}
// Round3 doesn't broadcast, so we don't implement BroadcastContent
// This ensures round3 doesn't implement the BroadcastRound interface
// Number implements round.Round
func (r *round3) Number() round.Number {
return 3
}
// MessageContent implements round.Round
func (r *round3) MessageContent() round.Content {
return nil // No messages in round 3
}
// VerifyMessage implements round.Round
func (r *round3) VerifyMessage(_ round.Message) error {
// No messages to verify
return nil
}
// StoreMessage implements round.Round
func (r *round3) StoreMessage(_ round.Message) error {
// No messages to store
return nil
}
// Finalize implements round.Round
func (r *round3) Finalize(_ chan<- *round.Message) (round.Session, error) {
// Verify we have shares from all parties
if len(r.shares) != r.N() {
return nil, errors.New("missing shares from some parties")
}
// Compute our final ECDSA share: sum of all shares received
ecdsaShare := r.Group().NewScalar()
for _, share := range r.shares {
ecdsaShare = ecdsaShare.Add(share)
}
// Build public shares map
// The public share for party j is the sum of all g^f_i(j)
publicShares := make(map[party.ID]*config.Public, r.N())
for _, j := range r.PartyIDs() {
publicPoint := r.Group().NewPoint()
for _, commitments := range r.commitments {
if commitment, ok := commitments[j]; ok {
publicPoint = publicPoint.Add(commitment)
}
}
publicShares[j] = &config.Public{
ECDSA: publicPoint,
}
}
// Compute combined chain key and RID
chainKeyData := make([][]byte, 0, r.N())
for _, id := range r.PartyIDs() {
if chainKey, ok := r.chainKeys[id]; ok {
chainKeyData = append(chainKeyData, chainKey[:])
}
}
// Hash all chain keys together for final chain key
h := hash.New()
for _, data := range chainKeyData {
_ = h.WriteAny(data)
}
finalChainKey := h.Sum()
// Create final RID by hashing session ID and chain key
ridHash := hash.New()
_ = ridHash.WriteAny(r.Hash())
_ = ridHash.WriteAny(finalChainKey)
finalRID := ridHash.Sum()
// Create the final config
cfg := &config.Config{
ID: r.SelfID(),
Group: r.Group(),
Threshold: r.Threshold(),
Generation: 0, // Initial generation
ECDSA: ecdsaShare,
Public: publicShares,
ChainKey: finalChainKey,
RID: finalRID,
}
// Validate the config before returning
if err := cfg.Validate(); err != nil {
return nil, err
}
// Verify that the public key can be recovered
if _, err := cfg.PublicPoint(); err != nil {
return nil, errors.New("failed to recover public key")
}
return r.ResultRound(cfg), nil
}
// Round3 doesn't need StoreBroadcastMessage since it's not a BroadcastRound