Files
threshold/protocols/cmp/presign/presign1.go
zeekay b41c78f5b3 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 (bf88f5a) 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

161 lines
4.8 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 presign
import (
"crypto/rand"
"github.com/luxfi/threshold/internal/elgamal"
"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/math/sample"
"github.com/luxfi/threshold/pkg/paillier"
"github.com/luxfi/threshold/pkg/party"
"github.com/luxfi/threshold/pkg/pedersen"
"github.com/luxfi/threshold/pkg/pool"
zkencelg "github.com/luxfi/threshold/pkg/zk/encelg"
)
var _ round.Round = (*presign1)(nil)
type presign1 struct {
*round.Base
// Pool allows us to parallelize certain operations
Pool *pool.Pool
// SecretECDSA = xᵢ
SecretECDSA curve.Scalar
// SecretElGamal = yᵢ
SecretElGamal curve.Scalar
// SecretPaillier = (pᵢ, qᵢ)
SecretPaillier *paillier.SecretKey
// PublicKey = X
PublicKey curve.Point
// ECDSA[j] = Xⱼ
ECDSA map[party.ID]curve.Point
// ElGamal[j] = Yⱼ
ElGamal map[party.ID]curve.Point
// Paillier[j] = Nⱼ
Paillier map[party.ID]*paillier.PublicKey
// Pedersen[j] = (Nⱼ,Sⱼ,Tⱼ)
Pedersen map[party.ID]*pedersen.Parameters
// Message is the message to be signed. If it is nil, a presignature is created.
Message []byte
}
// VerifyMessage implements round.Round.
func (*presign1) VerifyMessage(round.Message) error { return nil }
// StoreMessage implements round.Round.
func (*presign1) StoreMessage(round.Message) error { return nil }
// Finalize implements round.Round
//
// - sample kᵢ, γᵢ <- 𝔽,
// - Γᵢ = [γᵢ]⋅G
// - Gᵢ = Encᵢ(γᵢ;νᵢ)
// - Kᵢ = Encᵢ(kᵢ;ρᵢ)
//
// NOTE
// The protocol instructs us to broadcast Kᵢ and Gᵢ, but the protocol we implement
// cannot handle identify aborts since we are in a point to point model.
// We do as described in [LN18].
//
// In the next round, we send a hash of all the {Kⱼ,Gⱼ}ⱼ.
// In two rounds, we compare the hashes received and if they are different then we abort.
func (r *presign1) Finalize(out chan<- *round.Message) (round.Session, error) {
// γᵢ <- 𝔽,
GammaShare := sample.Scalar(rand.Reader, r.Group())
// Gᵢ = Encᵢ(γᵢ;νᵢ)
G, GNonce := r.Paillier[r.SelfID()].Enc(curve.MakeInt(GammaShare))
// kᵢ <- 𝔽,
KShare := sample.Scalar(rand.Reader, r.Group())
KShareInt := curve.MakeInt(KShare)
// Kᵢ = Encᵢ(kᵢ;ρᵢ)
K, KNonce := r.Paillier[r.SelfID()].Enc(KShareInt)
// Zᵢ = (bᵢ⋅G, kᵢ⋅G+bᵢ⋅Yᵢ), bᵢ
ElGamalK, ElGamalNonce := elgamal.Encrypt(r.ElGamal[r.SelfID()], KShare)
presignatureID, err := types.NewRID(rand.Reader)
if err != nil {
return r, err
}
commitmentID, decommitmentID, err := r.HashForID(r.SelfID()).Commit(presignatureID)
if err != nil {
return r, err
}
otherIDs := r.OtherPartyIDs()
broadcastMsg := broadcast2{
K: K,
G: G,
Z: ElGamalK,
CommitmentID: commitmentID,
}
if err = r.BroadcastMessage(out, &broadcastMsg); err != nil {
return r, err
}
errs := r.Pool.Parallelize(len(otherIDs), func(i int) interface{} {
j := otherIDs[i]
proof := zkencelg.NewProof(r.Group(), r.HashForID(r.SelfID()), zkencelg.Public{
C: K,
A: r.ElGamal[r.SelfID()],
B: ElGamalK.L,
X: ElGamalK.M,
Prover: r.Paillier[r.SelfID()],
Aux: r.Pedersen[j],
}, zkencelg.Private{
X: KShareInt,
Rho: KNonce,
A: r.SecretElGamal,
B: ElGamalNonce,
})
return r.SendMessage(out, &message2{Proof: proof}, j)
})
for _, err := range errs {
if err != nil {
return r, err.(error)
}
}
return &presign2{
presign1: r,
K: map[party.ID]*paillier.Ciphertext{r.SelfID(): K},
G: map[party.ID]*paillier.Ciphertext{r.SelfID(): G},
GammaShare: curve.MakeInt(GammaShare),
KShare: KShare,
KNonce: KNonce,
GNonce: GNonce,
ElGamalKNonce: ElGamalNonce,
ElGamalK: map[party.ID]*elgamal.Ciphertext{r.SelfID(): ElGamalK},
PresignatureID: map[party.ID]types.RID{r.SelfID(): presignatureID},
CommitmentID: map[party.ID]hash.Commitment{},
DecommitmentID: decommitmentID,
}, nil
}
// MessageContent implements round.Round.
func (*presign1) MessageContent() round.Content { return nil }
// Number implements round.Round.
func (*presign1) Number() round.Number { return 1 }
// BroadcastContent implements round.BroadcastRound.
// Note: presign1 sends broadcast2 messages in Finalize but must implement
// BroadcastContent to avoid the handler thinking no broadcasts are expected
// and finalizing immediately (handler.go line 364-365).
func (*presign1) BroadcastContent() round.BroadcastContent { return &broadcast2{} }
// StoreBroadcastMessage implements round.BroadcastRound.
// presign1 doesn't receive broadcasts, but must implement this to satisfy the interface.
func (r *presign1) StoreBroadcastMessage(msg round.Message) error {
// presign1 doesn't receive broadcasts
return nil
}