Files
threshold/protocols/cmp/sign/round2.go
zeekay bf88f5a56d round: pointer receivers on trivial round methods
VerifyMessage/StoreMessage/MessageContent/Number/BroadcastContent on
round structs were value receivers. Go FAQ: "if any method on T takes
*T, all methods should take *T." Finalize already takes *T (it mutates
round state) so the others should too. Aligns the method set, removes
silent struct-copies on every dispatch, and prevents future drift
where a value-receiver method accidentally observes pre-Finalize
state. No behavior change — all current methods are no-ops or return
constants.

Touched: round.Abort, round.Output, all CMP keygen/presign/sign rounds,
all Doerner keygen/sign rounds, all FROST keygen/sign rounds, xor
example. Build + vet clean; CMP/FROST/Doerner -race tests PASS.
2026-06-01 00:07:58 -07:00

190 lines
4.7 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 sign
import (
"errors"
"github.com/cronokirby/saferith"
"github.com/luxfi/threshold/internal/mta"
"github.com/luxfi/threshold/internal/round"
"github.com/luxfi/threshold/pkg/math/curve"
"github.com/luxfi/threshold/pkg/paillier"
"github.com/luxfi/threshold/pkg/party"
zkenc "github.com/luxfi/threshold/pkg/zk/enc"
zklogstar "github.com/luxfi/threshold/pkg/zk/logstar"
)
var _ round.Round = (*round2)(nil)
type round2 struct {
*round1
// K[j] = Kⱼ = encⱼ(kⱼ)
K map[party.ID]*paillier.Ciphertext
// G[j] = Gⱼ = encⱼ(γⱼ)
G map[party.ID]*paillier.Ciphertext
// BigGammaShare[j] = Γⱼ = [γⱼ]•G
BigGammaShare map[party.ID]curve.Point
// GammaShare = γᵢ <- 𝔽
GammaShare *saferith.Int
// KShare = kᵢ <- 𝔽
KShare curve.Scalar
// KNonce = ρᵢ <- ℤₙ
// used to encrypt Kᵢ = Encᵢ(kᵢ)
KNonce *saferith.Nat
// GNonce = νᵢ <- ℤₙ
// used to encrypt Gᵢ = Encᵢ(γᵢ)
GNonce *saferith.Nat
}
type broadcast2 struct {
round.ReliableBroadcastContent
// K = Kᵢ
K *paillier.Ciphertext
// G = Gᵢ
G *paillier.Ciphertext
}
type message2 struct {
ProofEnc *zkenc.Proof
}
// StoreBroadcastMessage implements round.Round.
//
// - store Kⱼ, Gⱼ.
func (r *round2) StoreBroadcastMessage(msg round.Message) error {
from := msg.From
body, ok := msg.Content.(*broadcast2)
if !ok || body == nil {
return round.ErrInvalidContent
}
if !r.Paillier[from].ValidateCiphertexts(body.K, body.G) {
return errors.New("invalid K, G")
}
r.K[from] = body.K
r.G[from] = body.G
return nil
}
// VerifyMessage implements round.Round.
//
// - verify zkenc(Kⱼ).
func (r *round2) VerifyMessage(msg round.Message) error {
from, to := msg.From, msg.To
body, ok := msg.Content.(*message2)
if !ok || body == nil {
return round.ErrInvalidContent
}
if body.ProofEnc == nil {
return round.ErrNilFields
}
if !body.ProofEnc.Verify(r.Group(), r.HashForID(from), zkenc.Public{
K: r.K[from],
Prover: r.Paillier[from],
Aux: r.Pedersen[to],
}) {
return errors.New("failed to validate enc proof for K")
}
return nil
}
// StoreMessage implements round.Round.
//
// - store Kⱼ, Gⱼ.
func (*round2) StoreMessage(round.Message) error { return nil }
// Finalize implements round.Round
//
// - compute Hash(ssid, K₁, G₁, …, Kₙ, Gₙ).
func (r *round2) Finalize(out chan<- *round.Message) (round.Session, error) {
if err := r.BroadcastMessage(out, &broadcast3{
BigGammaShare: r.BigGammaShare[r.SelfID()],
}); err != nil {
return r, err
}
otherIDs := r.OtherPartyIDs()
type mtaOut struct {
err error
DeltaBeta *saferith.Int
ChiBeta *saferith.Int
}
mtaOuts := r.Pool.Parallelize(len(otherIDs), func(i int) interface{} {
j := otherIDs[i]
DeltaBeta, DeltaD, DeltaF, DeltaProof := mta.ProveAffG(r.Group(), r.HashForID(r.SelfID()),
r.GammaShare, r.BigGammaShare[r.SelfID()], r.K[j],
r.SecretPaillier, r.Paillier[j], r.Pedersen[j])
ChiBeta, ChiD, ChiF, ChiProof := mta.ProveAffG(r.Group(),
r.HashForID(r.SelfID()), curve.MakeInt(r.SecretECDSA), r.ECDSA[r.SelfID()], r.K[j],
r.SecretPaillier, r.Paillier[j], r.Pedersen[j])
proof := zklogstar.NewProof(r.Group(), r.HashForID(r.SelfID()),
zklogstar.Public{
C: r.G[r.SelfID()],
X: r.BigGammaShare[r.SelfID()],
Prover: r.Paillier[r.SelfID()],
Aux: r.Pedersen[j],
}, zklogstar.Private{
X: r.GammaShare,
Rho: r.GNonce,
})
err := r.SendMessage(out, &message3{
DeltaD: DeltaD,
DeltaF: DeltaF,
DeltaProof: DeltaProof,
ChiD: ChiD,
ChiF: ChiF,
ChiProof: ChiProof,
ProofLog: proof,
}, j)
return mtaOut{
err: err,
DeltaBeta: DeltaBeta,
ChiBeta: ChiBeta,
}
})
DeltaShareBetas := make(map[party.ID]*saferith.Int, len(otherIDs)-1)
ChiShareBetas := make(map[party.ID]*saferith.Int, len(otherIDs)-1)
for idx, mtaOutRaw := range mtaOuts {
j := otherIDs[idx]
m := mtaOutRaw.(mtaOut)
if m.err != nil {
return r, m.err
}
DeltaShareBetas[j] = m.DeltaBeta
ChiShareBetas[j] = m.ChiBeta
}
return &round3{
round2: r,
DeltaShareBeta: DeltaShareBetas,
ChiShareBeta: ChiShareBetas,
DeltaShareAlpha: map[party.ID]*saferith.Int{},
ChiShareAlpha: map[party.ID]*saferith.Int{},
}, nil
}
// RoundNumber implements round.Content.
func (message2) RoundNumber() round.Number { return 2 }
// MessageContent implements round.Round.
func (*round2) MessageContent() round.Content { return &message2{} }
// RoundNumber implements round.Content.
func (broadcast2) RoundNumber() round.Number { return 2 }
// BroadcastContent implements round.BroadcastRound.
func (*round2) BroadcastContent() round.BroadcastContent { return &broadcast2{} }
// Number implements round.Round.
func (*round2) Number() round.Number { return 2 }