Files
zeekay f7b136528d 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

229 lines
5.9 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 (
"errors"
"github.com/cronokirby/saferith"
"github.com/luxfi/threshold/internal/elgamal"
"github.com/luxfi/threshold/internal/mta"
"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/paillier"
"github.com/luxfi/threshold/pkg/party"
zkaffg "github.com/luxfi/threshold/pkg/zk/affg"
zkaffp "github.com/luxfi/threshold/pkg/zk/affp"
zkencelg "github.com/luxfi/threshold/pkg/zk/encelg"
)
var _ round.Round = (*presign2)(nil)
type presign2 struct {
*presign1
// K[j] = Kⱼ = encⱼ(kⱼ)
K map[party.ID]*paillier.Ciphertext
// G[j] = Gⱼ = encⱼ(γⱼ)
G map[party.ID]*paillier.Ciphertext
// 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
// ElGamalKNonce = bᵢ
ElGamalKNonce elgamal.Nonce
// ElGamalK[j] = Zⱼ
ElGamalK map[party.ID]*elgamal.Ciphertext
// PresignatureID[j] = idⱼ
PresignatureID map[party.ID]types.RID
// CommitmentID[j] = Com(idⱼ)
CommitmentID map[party.ID]hash.Commitment
// DecommitmentID is the decommitment string for idᵢ
DecommitmentID hash.Decommitment
}
type broadcast2 struct {
round.ReliableBroadcastContent
// K = Kᵢ
K *paillier.Ciphertext
// G = Gᵢ
G *paillier.Ciphertext
// Z = Zᵢ
Z *elgamal.Ciphertext
// CommitmentID is a commitment Pᵢ's contribution to the final presignature ID.
CommitmentID hash.Commitment
}
type message2 struct {
Proof *zkencelg.Proof
}
// StoreBroadcastMessage implements round.BroadcastRound.
//
// - store Kⱼ, Gⱼ, Zⱼ, CommitmentID.
func (r *presign2) 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) || !body.Z.Valid() {
return round.ErrNilFields
}
if err := body.CommitmentID.Validate(); err != nil {
return err
}
r.K[from] = body.K
r.G[from] = body.G
r.ElGamalK[from] = body.Z
r.CommitmentID[from] = body.CommitmentID
return nil
}
// VerifyMessage implements round.Round.
//
// - verify zkencelg.
func (r *presign2) 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.Proof.Verify(r.HashForID(from), zkencelg.Public{
C: r.K[from],
A: r.ElGamal[from],
B: r.ElGamalK[from].L,
X: r.ElGamalK[from].M,
Prover: r.Paillier[from],
Aux: r.Pedersen[to],
}) {
return errors.New("failed to validate enc-elg proof for K")
}
return nil
}
// StoreMessage implements round.Round.
func (*presign2) StoreMessage(round.Message) error { return nil }
// Finalize implements round.Round
//
// Compute MtA for χᵢ, δᵢ
func (r *presign2) Finalize(out chan<- *round.Message) (round.Session, error) {
otherIDs := r.OtherPartyIDs()
n := len(otherIDs)
type mtaOut struct {
DeltaBeta *saferith.Int
DeltaD *paillier.Ciphertext
DeltaF *paillier.Ciphertext
DeltaProof *zkaffp.Proof
ChiBeta *saferith.Int
ChiD *paillier.Ciphertext
ChiF *paillier.Ciphertext
ChiProof *zkaffg.Proof
}
mtaOuts := r.Pool.Parallelize(len(otherIDs), func(i int) interface{} {
j := otherIDs[i]
DeltaBeta, DeltaD, DeltaF, DeltaProof := mta.ProveAffP(r.Group(), r.HashForID(r.SelfID()),
r.GammaShare, r.G[r.SelfID()], r.GNonce, 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])
return mtaOut{
DeltaBeta: DeltaBeta,
DeltaD: DeltaD,
DeltaF: DeltaF,
DeltaProof: DeltaProof,
ChiBeta: ChiBeta,
ChiD: ChiD,
ChiF: ChiF,
ChiProof: ChiProof,
}
})
ChiCiphertext := make(map[party.ID]*paillier.Ciphertext, n)
DeltaCiphertext := make(map[party.ID]*paillier.Ciphertext, n)
DeltaShareBeta := make(map[party.ID]*saferith.Int, n)
ChiShareBeta := make(map[party.ID]*saferith.Int, n)
broadcastMsg := broadcast3{
DeltaCiphertext: DeltaCiphertext,
ChiCiphertext: ChiCiphertext,
}
msgs := make(map[party.ID]*message3, n)
for idx, mtaOutRaw := range mtaOuts {
j := otherIDs[idx]
m := mtaOutRaw.(mtaOut)
DeltaShareBeta[j] = m.DeltaBeta
DeltaCiphertext[j] = m.DeltaD
ChiShareBeta[j] = m.ChiBeta
ChiCiphertext[j] = m.ChiD
msgs[j] = &message3{
DeltaF: m.DeltaF,
DeltaProof: m.DeltaProof,
ChiF: m.ChiF,
ChiProof: m.ChiProof,
}
}
if err := r.BroadcastMessage(out, &broadcastMsg); err != nil {
return r, err
}
for id, msg := range msgs {
if err := r.SendMessage(out, msg, id); err != nil {
return r, err
}
}
return &presign3{
presign2: r,
DeltaShareBeta: DeltaShareBeta,
ChiShareBeta: ChiShareBeta,
DeltaCiphertext: map[party.ID]map[party.ID]*paillier.Ciphertext{r.SelfID(): DeltaCiphertext},
ChiCiphertext: map[party.ID]map[party.ID]*paillier.Ciphertext{r.SelfID(): ChiCiphertext},
}, nil
}
// RoundNumber implements round.Content.
func (message2) RoundNumber() round.Number { return 2 }
// MessageContent implements round.Round.
func (r *presign2) MessageContent() round.Content {
return &message2{
Proof: zkencelg.Empty(r.Group()),
}
}
// RoundNumber implements round.Content.
func (broadcast2) RoundNumber() round.Number { return 2 }
// BroadcastContent implements round.BroadcastRound.
func (r *presign2) BroadcastContent() round.BroadcastContent {
return &broadcast2{
Z: elgamal.Empty(r.Group()),
}
}
// Number implements round.Round.
func (*presign2) Number() round.Number { return 2 }