@@ -221,45 +221,105 @@ func TestSetupThreshold(t *testing.T) {
221221 })
222222}
223223
224- // The threshold check also runs on quorumchange, which is built by a different
225- // constructor than keygen. This pins that DklsQcSetupMsgNew encodes the
226- // threshold the same way, so a library rebuild that diverges fails here rather
227- // than rejecting every quorum change in production.
228- func TestSetupThreshold_QuorumChange (t * testing.T ) {
224+ // Cross-protocol matrix over the three setup shapes the coordinator builds:
225+ // keygen (shared with keyrefresh), quorumchange, and sign (shared with fund
226+ // migration). Pins what each decoder returns for each shape, so a rebuilt DKLS
227+ // library that changes the encoding fails here rather than in production.
228+ func TestSetupDecoders_AllProtocols (t * testing.T ) {
229229 participants := []string {"party1" , "party2" , "party3" }
230230 ids := encodeParticipantIDs (participants )
231+ const threshold = 2
231232
232- kgSetup , err := session .DklsKeygenSetupMsgNew (2 , nil , ids )
233+ messageHash := make ([]byte , 32 )
234+ copy (messageHash , "outbound-signing-hash-32-bytes!!" )
235+
236+ // keygen, also used verbatim for keyrefresh
237+ keygenSetup , err := session .DklsKeygenSetupMsgNew (threshold , nil , ids )
233238 if err != nil {
234239 t .Fatalf ("failed to build keygen setup: %v" , err )
235240 }
241+
242+ // sign, also used for fund migration
243+ signSetup , err := session .DklsSignSetupMsgNew (make ([]byte , 32 ), nil , messageHash , ids )
244+ if err != nil {
245+ t .Fatalf ("failed to build sign setup: %v" , err )
246+ }
247+
248+ // quorumchange needs a real keyshare, so run a keygen to completion first
236249 sessions := map [string ]Session {}
237250 for _ , p := range participants {
238- s , err := NewKeygenSession (kgSetup , "threshold-qc " , p , participants , 2 )
251+ sess , err := NewKeygenSession (keygenSetup , "matrix " , p , participants , threshold )
239252 if err != nil {
240253 t .Fatalf ("failed to create keygen session for %s: %v" , p , err )
241254 }
242- sessions [p ] = s
255+ sessions [p ] = sess
243256 }
244257 keyshare := runToCompletion (t , sessions )["party1" ].Keyshare
245-
246258 handle , err := session .DklsKeyshareFromBytes (keyshare )
247259 if err != nil {
248260 t .Fatalf ("failed to load keyshare: %v" , err )
249261 }
250262 defer session .DklsKeyshareFree (handle )
251263
252- for _ , want := range []int {2 , 3 } {
253- qcSetup , err := session .DklsQcSetupMsgNew (handle , want , participants , []int {0 , 1 , 2 }, []int {0 , 1 , 2 })
254- if err != nil {
255- t .Fatalf ("failed to build QC setup with threshold %d: %v" , want , err )
256- }
257- got , err := SetupThreshold (qcSetup )
258- if err != nil {
259- t .Fatalf ("SetupThreshold(qc) error = %v" , err )
260- }
261- if got != want {
262- t .Errorf ("SetupThreshold(qc) = %d, want %d" , got , want )
263- }
264+ qcSetup , err := session .DklsQcSetupMsgNew (handle , threshold , participants , []int {0 , 1 , 2 }, []int {0 , 1 , 2 })
265+ if err != nil {
266+ t .Fatalf ("failed to build QC setup: %v" , err )
267+ }
268+
269+ shapes := []struct {
270+ name string
271+ setup []byte
272+ wantHash []byte // nil means the shape carries no message
273+ hasThreshold bool
274+ }{
275+ {"keygen and keyrefresh" , keygenSetup , nil , true },
276+ {"quorumchange" , qcSetup , nil , true },
277+ {"sign and fund migration" , signSetup , messageHash , false },
278+ }
279+
280+ for _ , sh := range shapes {
281+ t .Run (sh .name , func (t * testing.T ) {
282+ // Participants are bound for every protocol, so every shape must decode them.
283+ got , err := SetupParticipants (sh .setup )
284+ if err != nil {
285+ t .Fatalf ("SetupParticipants() error = %v" , err )
286+ }
287+ if len (got ) != len (participants ) {
288+ t .Fatalf ("SetupParticipants() = %v, want %v" , got , participants )
289+ }
290+ for i := range participants {
291+ if got [i ] != participants [i ] {
292+ t .Errorf ("participant %d = %q, want %q" , i , got [i ], participants [i ])
293+ }
294+ }
295+
296+ gotThreshold , thresholdErr := SetupThreshold (sh .setup )
297+ if sh .hasThreshold {
298+ if thresholdErr != nil {
299+ t .Fatalf ("SetupThreshold() error = %v" , thresholdErr )
300+ }
301+ if gotThreshold != threshold {
302+ t .Errorf ("SetupThreshold() = %d, want %d" , gotThreshold , threshold )
303+ }
304+ } else if thresholdErr == nil {
305+ // Sign setups carry no threshold. Erroring is what stops a bogus
306+ // value being read out of unrelated bytes.
307+ t .Errorf ("SetupThreshold() on a sign setup returned %d, want an error" , gotThreshold )
308+ }
309+
310+ gotHash , hashErr := SetupMessageHash (sh .setup )
311+ if hashErr != nil {
312+ t .Fatalf ("SetupMessageHash() error = %v" , hashErr )
313+ }
314+ if sh .wantHash == nil {
315+ // Shapes without a message report an empty hash rather than an
316+ // error, so a non-sign setup can never satisfy the hash binding.
317+ if len (gotHash ) != 0 {
318+ t .Errorf ("SetupMessageHash() = %x, want empty" , gotHash )
319+ }
320+ } else if ! bytes .Equal (gotHash , sh .wantHash ) {
321+ t .Errorf ("SetupMessageHash() = %x, want %x" , gotHash , sh .wantHash )
322+ }
323+ })
264324 }
265325}
0 commit comments