mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 00:27:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//
 | 
						|
// Copyright Coinbase, Inc. All Rights Reserved.
 | 
						|
//
 | 
						|
// SPDX-License-Identifier: Apache-2.0
 | 
						|
//
 | 
						|
 | 
						|
package frost
 | 
						|
 | 
						|
import (
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
 | 
						|
	dkg "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/dkg/frost"
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/sharing"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	testCurve = curves.ED25519()
 | 
						|
	ctx       = "string to prevent replay attack"
 | 
						|
)
 | 
						|
 | 
						|
// Create two DKG participants.
 | 
						|
func PrepareDkgOutput(t *testing.T) (*dkg.DkgParticipant, *dkg.DkgParticipant) {
 | 
						|
	// Initiate two participants and running DKG round 1
 | 
						|
	p1, err := dkg.NewDkgParticipant(1, 2, ctx, testCurve, 2)
 | 
						|
	require.NoError(t, err)
 | 
						|
	p2, err := dkg.NewDkgParticipant(2, 2, ctx, testCurve, 1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	bcast1, p2psend1, _ := p1.Round1(nil)
 | 
						|
	bcast2, p2psend2, _ := p2.Round1(nil)
 | 
						|
	bcast := make(map[uint32]*dkg.Round1Bcast)
 | 
						|
	p2p1 := make(map[uint32]*sharing.ShamirShare)
 | 
						|
	p2p2 := make(map[uint32]*sharing.ShamirShare)
 | 
						|
	bcast[1] = bcast1
 | 
						|
	bcast[2] = bcast2
 | 
						|
	p2p1[2] = p2psend2[1]
 | 
						|
	p2p2[1] = p2psend1[2]
 | 
						|
 | 
						|
	// Running DKG round 2
 | 
						|
	_, _ = p1.Round2(bcast, p2p1)
 | 
						|
	_, _ = p2.Round2(bcast, p2p2)
 | 
						|
	return p1, p2
 | 
						|
}
 | 
						|
 | 
						|
// Test FROST signing round 1
 | 
						|
func TestSignRound1Works(t *testing.T) {
 | 
						|
	p1, p2 := PrepareDkgOutput(t)
 | 
						|
	require.NotNil(t, p1)
 | 
						|
	require.NotNil(t, p2)
 | 
						|
 | 
						|
	scheme, _ := sharing.NewShamir(2, 2, testCurve)
 | 
						|
	lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
 | 
						|
	require.NoError(t, err)
 | 
						|
	signer1, err := NewSigner(p1, 1, 2, lCoeffs, []uint32{1, 2}, &Ed25519ChallengeDeriver{})
 | 
						|
	require.NoError(t, err)
 | 
						|
	round1Out, _ := signer1.SignRound1()
 | 
						|
	require.NotNil(t, round1Out.Ei)
 | 
						|
	require.NotNil(t, round1Out.Di)
 | 
						|
	require.NotNil(t, signer1.state.smallE)
 | 
						|
	require.NotNil(t, signer1.state.smallD)
 | 
						|
	require.NotNil(t, signer1.state.capD)
 | 
						|
	require.NotNil(t, signer1.state.capE)
 | 
						|
	require.Equal(t, signer1.round, uint(2))
 | 
						|
	require.Equal(t, signer1.cosigners, []uint32{1, 2})
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound1RepeatCall(t *testing.T) {
 | 
						|
	p1, p2 := PrepareDkgOutput(t)
 | 
						|
	scheme, _ := sharing.NewShamir(2, 2, testCurve)
 | 
						|
	lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
 | 
						|
	require.NoError(t, err)
 | 
						|
	signer1, _ := NewSigner(p1, 1, 2, lCoeffs, []uint32{1, 2}, &Ed25519ChallengeDeriver{})
 | 
						|
	_, err = signer1.SignRound1()
 | 
						|
	require.NoError(t, err)
 | 
						|
	_, err = signer1.SignRound1()
 | 
						|
	require.Error(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func PrepareNewSigners(t *testing.T) (*Signer, *Signer) {
 | 
						|
	threshold := uint32(2)
 | 
						|
	limit := uint32(2)
 | 
						|
	p1, p2 := PrepareDkgOutput(t)
 | 
						|
	require.Equal(t, p1.VerificationKey, p2.VerificationKey)
 | 
						|
	scheme, err := sharing.NewShamir(threshold, limit, testCurve)
 | 
						|
	// field = sharing.NewField(p1.curve.Params().N)
 | 
						|
	require.NotNil(t, scheme)
 | 
						|
	require.NoError(t, err)
 | 
						|
	lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
 | 
						|
	require.NotNil(t, lCoeffs[1])
 | 
						|
	require.NotNil(t, lCoeffs[2])
 | 
						|
	require.NoError(t, err)
 | 
						|
	signer1, err := NewSigner(p1, p1.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
 | 
						|
	require.NotNil(t, signer1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	signer2, err := NewSigner(p2, p2.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
 | 
						|
	require.NotNil(t, signer2)
 | 
						|
	require.NoError(t, err)
 | 
						|
	return signer1, signer2
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound2Works(t *testing.T) {
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 := PrepareNewSigners(t)
 | 
						|
	require.Equal(t, signer1.verificationKey, signer2.verificationKey)
 | 
						|
	require.NotNil(t, signer1)
 | 
						|
	require.NotNil(t, signer2)
 | 
						|
	round1Out1, _ := signer1.SignRound1()
 | 
						|
	round1Out2, _ := signer2.SignRound1()
 | 
						|
	round2Input := make(map[uint32]*Round1Bcast)
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
 | 
						|
	// Actual Test
 | 
						|
	msg := []byte("message")
 | 
						|
	round2Out, err := signer1.SignRound2(msg, round2Input)
 | 
						|
	require.NotNil(t, round2Out)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.Equal(t, signer1.round, uint(3))
 | 
						|
	require.NotNil(t, signer1.state.commitments)
 | 
						|
	require.Equal(t, signer1.state.msg, msg)
 | 
						|
	require.True(t, signer1.state.smallD.IsZero())
 | 
						|
	require.True(t, signer1.state.smallE.IsZero())
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound2RepeatCall(t *testing.T) {
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 := PrepareNewSigners(t)
 | 
						|
	require.NotNil(t, signer1)
 | 
						|
	require.NotNil(t, signer2)
 | 
						|
	round1Out1, _ := signer1.SignRound1()
 | 
						|
	round1Out2, _ := signer2.SignRound1()
 | 
						|
	round2Input := make(map[uint32]*Round1Bcast)
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
 | 
						|
	// Actual Test
 | 
						|
	msg := []byte("message")
 | 
						|
	_, err := signer1.SignRound2(msg, round2Input)
 | 
						|
	require.NoError(t, err)
 | 
						|
	_, err = signer1.SignRound2(msg, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound2BadInput(t *testing.T) {
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 := PrepareNewSigners(t)
 | 
						|
	_, _ = signer1.SignRound1()
 | 
						|
	round1Out2, _ := signer2.SignRound1()
 | 
						|
	round2Input := make(map[uint32]*Round1Bcast)
 | 
						|
 | 
						|
	// Actual Test: Set an input to nil
 | 
						|
	round2Input[signer1.id] = nil
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
	msg := []byte("message")
 | 
						|
	_, err := signer1.SignRound2(msg, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 = PrepareNewSigners(t)
 | 
						|
	round1Out1, _ := signer1.SignRound1()
 | 
						|
	round1Out2, _ = signer2.SignRound1()
 | 
						|
	round2Input = make(map[uint32]*Round1Bcast)
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
	// Actual Test: Nil message
 | 
						|
	_, err = signer1.SignRound2(nil, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 = PrepareNewSigners(t)
 | 
						|
	round1Out1, _ = signer1.SignRound1()
 | 
						|
	round1Out2, _ = signer2.SignRound1()
 | 
						|
	round2Input = make(map[uint32]*Round1Bcast)
 | 
						|
 | 
						|
	// Actual Test: Set invalid round2Input length
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
	round2Input[3] = round1Out2
 | 
						|
	_, err = signer1.SignRound2(msg, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 = PrepareNewSigners(t)
 | 
						|
	round1Out1, _ = signer1.SignRound1()
 | 
						|
	round1Out2, _ = signer2.SignRound1()
 | 
						|
	round2Input = make(map[uint32]*Round1Bcast)
 | 
						|
 | 
						|
	// Actual Test: Set invalid round2Input length
 | 
						|
	round1Out2.Ei = nil
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
	_, err = signer1.SignRound2(msg, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Preparing round 2 inputs
 | 
						|
	signer1, signer2 = PrepareNewSigners(t)
 | 
						|
	round1Out1, _ = signer1.SignRound1()
 | 
						|
	round1Out2, _ = signer2.SignRound1()
 | 
						|
	round2Input = make(map[uint32]*Round1Bcast)
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
 | 
						|
	// Actual Test: Set nil smallD and smallE
 | 
						|
	signer1.state.smallD = testCurve.NewScalar()
 | 
						|
	signer1.state.smallE = nil
 | 
						|
	_, err = signer1.SignRound2(msg, round2Input)
 | 
						|
	require.Error(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func PrepareRound3Input(t *testing.T) (*Signer, *Signer, map[uint32]*Round2Bcast) {
 | 
						|
	// Running sign round 1
 | 
						|
	threshold := uint32(2)
 | 
						|
	limit := uint32(2)
 | 
						|
	p1, p2 := PrepareDkgOutput(t)
 | 
						|
	require.Equal(t, p1.VerificationKey, p2.VerificationKey)
 | 
						|
	scheme, err := sharing.NewShamir(threshold, limit, testCurve)
 | 
						|
	require.NotNil(t, scheme)
 | 
						|
	require.NoError(t, err)
 | 
						|
	lCoeffs, err := scheme.LagrangeCoeffs([]uint32{p1.Id, p2.Id})
 | 
						|
	require.NotNil(t, lCoeffs[1])
 | 
						|
	require.NotNil(t, lCoeffs[2])
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	signer1, err := NewSigner(p1, p1.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
 | 
						|
	require.NotNil(t, signer1)
 | 
						|
	require.NoError(t, err)
 | 
						|
	signer2, err := NewSigner(p2, p2.Id, threshold, lCoeffs, []uint32{p1.Id, p2.Id}, &Ed25519ChallengeDeriver{})
 | 
						|
	require.NotNil(t, signer2)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	round1Out1, _ := signer1.SignRound1()
 | 
						|
	round1Out2, _ := signer2.SignRound1()
 | 
						|
	round2Input := make(map[uint32]*Round1Bcast, threshold)
 | 
						|
	round2Input[signer1.id] = round1Out1
 | 
						|
	round2Input[signer2.id] = round1Out2
 | 
						|
 | 
						|
	// Running sign round 2
 | 
						|
	msg := []byte("message")
 | 
						|
	round2Out1, _ := signer1.SignRound2(msg, round2Input)
 | 
						|
	round2Out2, _ := signer2.SignRound2(msg, round2Input)
 | 
						|
	round3Input := make(map[uint32]*Round2Bcast, threshold)
 | 
						|
	round3Input[signer1.id] = round2Out1
 | 
						|
	round3Input[signer2.id] = round2Out2
 | 
						|
	return signer1, signer2, round3Input
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound3Works(t *testing.T) {
 | 
						|
	signer1, signer2, round3Input := PrepareRound3Input(t)
 | 
						|
	round3Out1, err := signer1.SignRound3(round3Input)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NotNil(t, round3Out1)
 | 
						|
	round3Out2, err := signer2.SignRound3(round3Input)
 | 
						|
	require.NoError(t, err)
 | 
						|
	require.NotNil(t, round3Out2)
 | 
						|
	// signer1 and signer2 outputs the same signature
 | 
						|
	require.Equal(t, round3Out1.Z, round3Out2.Z)
 | 
						|
	require.Equal(t, round3Out1.C, round3Out2.C)
 | 
						|
 | 
						|
	// test verify method
 | 
						|
	msg := []byte("message")
 | 
						|
	signature := &Signature{
 | 
						|
		round3Out1.Z,
 | 
						|
		round3Out1.C,
 | 
						|
	}
 | 
						|
	vk := signer1.verificationKey
 | 
						|
	ok, err := Verify(signer1.curve, signer1.challengeDeriver, vk, msg, signature)
 | 
						|
	require.True(t, ok)
 | 
						|
	require.NoError(t, err)
 | 
						|
 | 
						|
	ok, err = Verify(signer2.curve, signer2.challengeDeriver, vk, msg, signature)
 | 
						|
	require.True(t, ok)
 | 
						|
	require.NoError(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound3RepeatCall(t *testing.T) {
 | 
						|
	signer1, _, round3Input := PrepareRound3Input(t)
 | 
						|
	_, err := signer1.SignRound3(round3Input)
 | 
						|
	require.NoError(t, err)
 | 
						|
	_, err = signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func TestSignRound3BadInput(t *testing.T) {
 | 
						|
	signer1, _, round3Input := PrepareRound3Input(t)
 | 
						|
 | 
						|
	// Actual test: nil input
 | 
						|
	round3Input[signer1.id] = nil
 | 
						|
	_, err := signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
	round3Input = nil
 | 
						|
	_, err = signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Actual test: set invalid length of round3Input
 | 
						|
	signer1, _, round3Input = PrepareRound3Input(t)
 | 
						|
	round3Input[100] = round3Input[signer1.id]
 | 
						|
	_, err = signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Actual test: maul the round3Input
 | 
						|
	signer1, _, round3Input = PrepareRound3Input(t)
 | 
						|
	round3Input[signer1.id].Zi = round3Input[signer1.id].Zi.Add(testCurve.Scalar.New(2))
 | 
						|
	_, err = signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
 | 
						|
	// Actual test: set non-zero smallD and smallE
 | 
						|
	signer1, _, round3Input = PrepareRound3Input(t)
 | 
						|
	signer1.state.smallD = testCurve.Scalar.New(1)
 | 
						|
	_, err = signer1.SignRound3(round3Input)
 | 
						|
	require.Error(t, err)
 | 
						|
}
 | 
						|
 | 
						|
func TestFullRoundsWorks(t *testing.T) {
 | 
						|
	// Give a full-round test (FROST DKG + FROST Signing) with threshold = 2 and limit = 3, same as the test of tECDSA
 | 
						|
	threshold := 2
 | 
						|
	limit := 3
 | 
						|
 | 
						|
	// Prepare DKG participants
 | 
						|
	participants := make(map[uint32]*dkg.DkgParticipant, limit)
 | 
						|
	for i := 1; i <= limit; i++ {
 | 
						|
		otherIds := make([]uint32, limit-1)
 | 
						|
		idx := 0
 | 
						|
		for j := 1; j <= limit; j++ {
 | 
						|
			if i == j {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			otherIds[idx] = uint32(j)
 | 
						|
			idx++
 | 
						|
		}
 | 
						|
		p, err := dkg.NewDkgParticipant(uint32(i), uint32(threshold), ctx, testCurve, otherIds...)
 | 
						|
		require.NoError(t, err)
 | 
						|
		participants[uint32(i)] = p
 | 
						|
	}
 | 
						|
 | 
						|
	// FROST DKG round 1
 | 
						|
	rnd1Bcast := make(map[uint32]*dkg.Round1Bcast, len(participants))
 | 
						|
	rnd1P2p := make(map[uint32]dkg.Round1P2PSend, len(participants))
 | 
						|
	for id, p := range participants {
 | 
						|
		bcast, p2psend, err := p.Round1(nil)
 | 
						|
		require.NoError(t, err)
 | 
						|
		rnd1Bcast[id] = bcast
 | 
						|
		rnd1P2p[id] = p2psend
 | 
						|
	}
 | 
						|
 | 
						|
	// FROST DKG round 2
 | 
						|
	for id := range rnd1Bcast {
 | 
						|
		rnd1P2pForP := make(map[uint32]*sharing.ShamirShare)
 | 
						|
		for jid := range rnd1P2p {
 | 
						|
			if jid == id {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			rnd1P2pForP[jid] = rnd1P2p[jid][id]
 | 
						|
		}
 | 
						|
		_, err := participants[id].Round2(rnd1Bcast, rnd1P2pForP)
 | 
						|
		require.NoError(t, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Prepare Lagrange coefficients
 | 
						|
	scheme, _ := sharing.NewShamir(uint32(threshold), uint32(limit), testCurve)
 | 
						|
 | 
						|
	// Here we use {1, 3} as 2 of 3 cosigners, we can also set cosigners as {1, 2}, {2, 3}
 | 
						|
	signerIds := []uint32{1, 3}
 | 
						|
	lCoeffs, err := scheme.LagrangeCoeffs(signerIds)
 | 
						|
	require.NoError(t, err)
 | 
						|
	signers := make(map[uint32]*Signer, threshold)
 | 
						|
	for _, id := range signerIds {
 | 
						|
		signers[id], err = NewSigner(participants[id], id, uint32(threshold), lCoeffs, signerIds, &Ed25519ChallengeDeriver{})
 | 
						|
		require.NoError(t, err)
 | 
						|
		require.NotNil(t, signers[id].skShare)
 | 
						|
	}
 | 
						|
 | 
						|
	// Running sign round 1
 | 
						|
	round2Input := make(map[uint32]*Round1Bcast, threshold)
 | 
						|
	for id := range signers {
 | 
						|
		round1Out, err := signers[id].SignRound1()
 | 
						|
		require.NoError(t, err)
 | 
						|
		round2Input[signers[id].id] = round1Out
 | 
						|
	}
 | 
						|
 | 
						|
	// Running sign round 2
 | 
						|
	msg := []byte("message")
 | 
						|
	round3Input := make(map[uint32]*Round2Bcast, threshold)
 | 
						|
	for id := range signers {
 | 
						|
		round2Out, err := signers[id].SignRound2(msg, round2Input)
 | 
						|
		require.NoError(t, err)
 | 
						|
		round3Input[signers[id].id] = round2Out
 | 
						|
	}
 | 
						|
 | 
						|
	// Running sign round 3
 | 
						|
	result := make(map[uint32]*Round3Bcast, threshold)
 | 
						|
	for id := range signers {
 | 
						|
		round3Out, err := signers[id].SignRound3(round3Input)
 | 
						|
		require.NoError(t, err)
 | 
						|
		result[signers[id].id] = round3Out
 | 
						|
	}
 | 
						|
 | 
						|
	// Every signer has the same output Schnorr signature
 | 
						|
	require.Equal(t, result[1].Z, result[3].Z)
 | 
						|
	// require.Equal(t, z, result[3].Z)
 | 
						|
	require.Equal(t, result[1].C, result[3].C)
 | 
						|
	// require.Equal(t, c, result[3].C)
 | 
						|
}
 |