mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
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.
170 lines
4.4 KiB
Go
170 lines
4.4 KiB
Go
package presign
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/cronokirby/saferith"
|
|
"github.com/luxfi/threshold/internal/round"
|
|
"github.com/luxfi/threshold/pkg/math/curve"
|
|
"github.com/luxfi/threshold/pkg/party"
|
|
zkelog "github.com/luxfi/threshold/pkg/zk/elog"
|
|
)
|
|
|
|
var _ round.Round = (*presign6)(nil)
|
|
|
|
type presign6 struct {
|
|
*presign5
|
|
|
|
// BigDeltaShares[j] = Δⱼ = [kⱼ]•Γⱼ
|
|
BigDeltaShares map[party.ID]curve.Point
|
|
|
|
// Gamma = ∑ᵢ Γᵢ
|
|
Gamma curve.Point
|
|
}
|
|
|
|
type broadcast6 struct {
|
|
round.NormalBroadcastContent
|
|
// BigDeltaShare = Δⱼ = [kⱼ]•Γⱼ
|
|
BigDeltaShare curve.Point
|
|
Proof *zkelog.Proof
|
|
}
|
|
|
|
// StoreBroadcastMessage implements round.BroadcastRound.
|
|
//
|
|
// - save Δⱼ and verify proof.
|
|
func (r *presign6) StoreBroadcastMessage(msg round.Message) error {
|
|
from := msg.From
|
|
body, ok := msg.Content.(*broadcast6)
|
|
if !ok || body == nil {
|
|
return round.ErrInvalidContent
|
|
}
|
|
|
|
if body.BigDeltaShare.IsIdentity() {
|
|
return round.ErrNilFields
|
|
}
|
|
|
|
if !body.Proof.Verify(r.HashForID(from), zkelog.Public{
|
|
E: r.ElGamalK[from],
|
|
ElGamalPublic: r.ElGamal[from],
|
|
Base: r.Gamma,
|
|
Y: body.BigDeltaShare,
|
|
}) {
|
|
return errors.New("failed to validate elog proof for BigDeltaShare")
|
|
}
|
|
|
|
r.BigDeltaShares[from] = body.BigDeltaShare
|
|
return nil
|
|
}
|
|
|
|
// VerifyMessage implements round.Round.
|
|
func (*presign6) VerifyMessage(round.Message) error { return nil }
|
|
|
|
// StoreMessage implements round.Round.
|
|
func (r *presign6) StoreMessage(_ round.Message) error { return nil }
|
|
|
|
// Finalize implements round.Round
|
|
//
|
|
// - compute δ = ∑ⱼ δⱼ,
|
|
// - compute R = [δ⁻¹] Γ,
|
|
// - compute Sᵢ = χᵢ⋅R,
|
|
// - compute {R̄ⱼ = δ⁻¹⋅Δⱼ}ⱼ.
|
|
func (r *presign6) Finalize(out chan<- *round.Message) (round.Session, error) {
|
|
// δ = ∑ⱼ δⱼ
|
|
Delta := r.Group().NewScalar()
|
|
for _, DeltaJ := range r.DeltaShares {
|
|
Delta.Add(DeltaJ)
|
|
}
|
|
|
|
// δ⁻¹
|
|
DeltaInv := r.Group().NewScalar().Set(Delta).Invert()
|
|
|
|
// R = [δ⁻¹] Γ
|
|
R := DeltaInv.Act(r.Gamma)
|
|
|
|
// δ⋅G
|
|
BigDeltaExpected := Delta.ActOnBase()
|
|
|
|
// ∑ⱼΔⱼ
|
|
BigDeltaActual := r.Group().NewPoint()
|
|
for _, BigDeltaJ := range r.BigDeltaShares {
|
|
BigDeltaActual = BigDeltaActual.Add(BigDeltaJ)
|
|
}
|
|
|
|
// δ⋅G ?= ∑ⱼΔⱼ
|
|
if !BigDeltaActual.Equal(BigDeltaExpected) {
|
|
DeltaProofs := make(map[party.ID]*abortNth, r.N()-1)
|
|
for _, j := range r.OtherPartyIDs() {
|
|
deltaCiphertext := r.DeltaCiphertext[j][r.SelfID()] // Dᵢⱼ
|
|
DeltaProofs[j] = proveNth(r.HashForID(r.SelfID()), r.SecretPaillier, deltaCiphertext)
|
|
}
|
|
msg := &broadcastAbort1{
|
|
GammaShare: r.GammaShare,
|
|
KProof: proveNth(r.HashForID(r.SelfID()), r.SecretPaillier, r.K[r.SelfID()]),
|
|
DeltaProofs: DeltaProofs,
|
|
}
|
|
if err := r.BroadcastMessage(out, msg); err != nil {
|
|
return r, err
|
|
}
|
|
return &abort1{
|
|
presign6: r,
|
|
GammaShares: map[party.ID]*saferith.Int{r.SelfID(): r.GammaShare},
|
|
KShares: map[party.ID]*saferith.Int{r.SelfID(): curve.MakeInt(r.KShare)},
|
|
DeltaAlphas: map[party.ID]map[party.ID]*saferith.Int{r.SelfID(): r.DeltaShareAlpha},
|
|
}, nil
|
|
}
|
|
|
|
// Sᵢ = χᵢ⋅R,
|
|
S := r.ChiShare.Act(R)
|
|
|
|
// {R̄ⱼ = δ⁻¹⋅Δⱼ}ⱼ
|
|
RBar := make(map[party.ID]curve.Point, r.N())
|
|
for j, BigDeltaJ := range r.BigDeltaShares {
|
|
RBar[j] = DeltaInv.Act(BigDeltaJ)
|
|
}
|
|
|
|
proof := zkelog.NewProof(r.Group(), r.HashForID(r.SelfID()), zkelog.Public{
|
|
E: r.ElGamalChi[r.SelfID()],
|
|
ElGamalPublic: r.ElGamal[r.SelfID()],
|
|
Base: R,
|
|
Y: S,
|
|
}, zkelog.Private{
|
|
Y: r.ChiShare,
|
|
Lambda: r.ElGamalChiNonce,
|
|
})
|
|
|
|
err := r.BroadcastMessage(out, &broadcast7{
|
|
S: S,
|
|
Proof: proof,
|
|
DecommitmentID: r.DecommitmentID,
|
|
PresignatureID: r.PresignatureID[r.SelfID()],
|
|
})
|
|
if err != nil {
|
|
return r, err
|
|
}
|
|
|
|
return &presign7{
|
|
presign6: r,
|
|
Delta: Delta,
|
|
S: map[party.ID]curve.Point{r.SelfID(): S},
|
|
R: R,
|
|
RBar: RBar,
|
|
}, nil
|
|
}
|
|
|
|
// MessageContent implements round.Round.
|
|
func (*presign6) MessageContent() round.Content { return nil }
|
|
|
|
// RoundNumber implements round.Content.
|
|
func (broadcast6) RoundNumber() round.Number { return 6 }
|
|
|
|
// BroadcastContent implements round.BroadcastRound.
|
|
func (r *presign6) BroadcastContent() round.BroadcastContent {
|
|
return &broadcast6{
|
|
BigDeltaShare: r.Group().NewPoint(),
|
|
Proof: zkelog.Empty(r.Group()),
|
|
}
|
|
}
|
|
|
|
// Number implements round.Round.
|
|
func (*presign6) Number() round.Number { return 6 }
|