github.com/onflow/flow-go/crypto@v0.24.8/dkg_test.go (about)

     1  //go:build relic
     2  // +build relic
     3  
     4  package crypto
     5  
     6  import (
     7  	crand "crypto/rand"
     8  	"fmt"
     9  	mrand "math/rand"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	log "github.com/sirupsen/logrus"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  var gt *testing.T
    20  
    21  func TestDKG(t *testing.T) {
    22  	t.Run("FeldmanVSSSimple", testFeldmanVSSSimple)
    23  	t.Run("FeldmanVSSQual", testFeldmanVSSQual)
    24  	t.Run("JointFeldman", testJointFeldman)
    25  }
    26  
    27  // optimal threshold (t) to allow the largest number of malicious participants (m)
    28  // assuming the protocol requires:
    29  //
    30  //	m<=t for unforgeability
    31  //	n-m>=t+1 for robustness
    32  func optimalThreshold(size int) int {
    33  	return (size - 1) / 2
    34  }
    35  
    36  // Testing the happy path of Feldman VSS by simulating a network of n participants
    37  func testFeldmanVSSSimple(t *testing.T) {
    38  	log.SetLevel(log.ErrorLevel)
    39  
    40  	n := 4
    41  	for threshold := MinimumThreshold; threshold < n; threshold++ {
    42  		t.Run(fmt.Sprintf("FeldmanVSS (n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
    43  			dkgCommonTest(t, feldmanVSS, n, threshold, happyPath)
    44  		})
    45  	}
    46  }
    47  
    48  type testCase int
    49  
    50  const (
    51  	happyPath testCase = iota
    52  	invalidShares
    53  	invalidVector
    54  	invalidComplaint
    55  	invalidComplaintAnswer
    56  	duplicatedMessages
    57  )
    58  
    59  type behavior int
    60  
    61  const (
    62  	honest behavior = iota
    63  	manyInvalidShares
    64  	fewInvalidShares
    65  	invalidVectorBroadcast
    66  	invalidComplaintBroadcast
    67  	timeoutedComplaintBroadcast
    68  	invalidSharesComplainTrigger
    69  	invalidComplaintAnswerBroadcast
    70  	duplicatedSendAndBroadcast
    71  )
    72  
    73  // Testing Feldman VSS with the qualification system by simulating a network of n participants
    74  func testFeldmanVSSQual(t *testing.T) {
    75  	log.SetLevel(log.ErrorLevel)
    76  
    77  	n := 4
    78  	// happy path, test multiple values of thresold
    79  	for threshold := MinimumThreshold; threshold < n; threshold++ {
    80  		t.Run(fmt.Sprintf("FeldmanVSSQual_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
    81  			dkgCommonTest(t, feldmanVSSQual, n, threshold, happyPath)
    82  		})
    83  	}
    84  
    85  	// unhappy path, with focus on the optimal threshold value
    86  	n = 5
    87  	threshold := optimalThreshold(n)
    88  	// unhappy path, with invalid shares
    89  	t.Run(fmt.Sprintf("FeldmanVSSQual_InvalidShares_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
    90  		dkgCommonTest(t, feldmanVSSQual, n, threshold, invalidShares)
    91  	})
    92  	// unhappy path, with invalid vector
    93  	t.Run(fmt.Sprintf("FeldmanVSSQual_InvalidVector_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
    94  		dkgCommonTest(t, feldmanVSSQual, n, threshold, invalidVector)
    95  	})
    96  	// unhappy paths with invalid complaints and complaint answers
    97  	// are only tested within joint feldman.
    98  }
    99  
   100  // Testing JointFeldman by simulating a network of n participants
   101  func testJointFeldman(t *testing.T) {
   102  	log.SetLevel(log.ErrorLevel)
   103  
   104  	n := 4
   105  	var threshold int
   106  	// happy path, test multiple values of thresold
   107  	for threshold = MinimumThreshold; threshold < n; threshold++ {
   108  		t.Run(fmt.Sprintf("JointFeldman_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   109  			dkgCommonTest(t, jointFeldman, n, threshold, happyPath)
   110  		})
   111  	}
   112  
   113  	// unhappy path, with focus on the optimal threshold value
   114  	n = 5
   115  	threshold = optimalThreshold(n)
   116  	// unhappy path, with invalid shares
   117  	t.Run(fmt.Sprintf("JointFeldman_InvalidShares_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   118  		dkgCommonTest(t, jointFeldman, n, threshold, invalidShares)
   119  	})
   120  	// unhappy path, with invalid vector
   121  	t.Run(fmt.Sprintf("JointFeldman_InvalidVector_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   122  		dkgCommonTest(t, jointFeldman, n, threshold, invalidVector)
   123  	})
   124  	// unhappy path, with invalid complaints
   125  	t.Run(fmt.Sprintf("JointFeldman_InvalidComplaints_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   126  		dkgCommonTest(t, jointFeldman, n, threshold, invalidComplaint)
   127  	})
   128  	// unhappy path, with invalid complaint answers
   129  	t.Run(fmt.Sprintf("JointFeldman_InvalidComplaintAnswers_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   130  		dkgCommonTest(t, jointFeldman, n, threshold, invalidComplaintAnswer)
   131  	})
   132  	// unhappy path, with duplicated messages (all types)
   133  	t.Run(fmt.Sprintf("JointFeldman_DuplicatedMessages_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) {
   134  		dkgCommonTest(t, jointFeldman, n, threshold, duplicatedMessages)
   135  	})
   136  }
   137  
   138  // Supported Key Generation protocols
   139  const (
   140  	feldmanVSS = iota
   141  	feldmanVSSQual
   142  	jointFeldman
   143  )
   144  
   145  func newDKG(dkg int, size int, threshold int, myIndex int,
   146  	processor DKGProcessor, dealerIndex int) (DKGState, error) {
   147  	switch dkg {
   148  	case feldmanVSS:
   149  		return NewFeldmanVSS(size, threshold, myIndex, processor, dealerIndex)
   150  	case feldmanVSSQual:
   151  		return NewFeldmanVSSQual(size, threshold, myIndex, processor, dealerIndex)
   152  	case jointFeldman:
   153  		return NewJointFeldman(size, threshold, myIndex, processor)
   154  	default:
   155  		return nil, fmt.Errorf("non supported protocol")
   156  	}
   157  }
   158  
   159  func dkgCommonTest(t *testing.T, dkg int, n int, threshold int, test testCase) {
   160  	gt = t
   161  	log.Info("DKG protocol set up")
   162  
   163  	// create the participant channels
   164  	chans := make([]chan *message, n)
   165  	lateChansTimeout1 := make([]chan *message, n)
   166  	lateChansTimeout2 := make([]chan *message, n)
   167  	for i := 0; i < n; i++ {
   168  		chans[i] = make(chan *message, 5*n)
   169  		lateChansTimeout1[i] = make(chan *message, 5*n)
   170  		lateChansTimeout2[i] = make(chan *message, 5*n)
   171  	}
   172  
   173  	// number of dealers in the protocol
   174  	var dealers int
   175  	if dkg == jointFeldman {
   176  		dealers = n
   177  	} else {
   178  		dealers = 1
   179  	}
   180  
   181  	// create n processors for all participants
   182  	processors := make([]testDKGProcessor, 0, n)
   183  	for current := 0; current < n; current++ {
   184  		list := make([]bool, dealers)
   185  		processors = append(processors, testDKGProcessor{
   186  			current:           current,
   187  			chans:             chans,
   188  			lateChansTimeout1: lateChansTimeout1,
   189  			lateChansTimeout2: lateChansTimeout2,
   190  			protocol:          dkgType,
   191  			malicious:         honest,
   192  			disqualified:      list,
   193  		})
   194  	}
   195  
   196  	// Update processors depending on the test
   197  	//
   198  	// r1 and r2 is the number of malicious participants, each group with a slight diffrent behavior.
   199  	// - r1 participants of indices 0 to r1-1 behave maliciously and will get disqualified by honest participants.
   200  	// - r2 participants of indices r1 to r1+r2-1 will behave maliciously at first but will recover and won't be
   201  	// disqualified by honest participants. The r2 participants may or may not obtain correct protocol results.
   202  	var r1, r2 int
   203  	// h is the index of the first honest participant. All participant with indices greater than or equal to h are honest.
   204  	// Checking the final protocol results is done for honest participants only.
   205  	// Whether the r2 participants belong to the honest participants or not depend on the malicious behavior (detailed below).
   206  	var h int
   207  
   208  	switch test {
   209  	case happyPath:
   210  		// r1 = r2 = 0
   211  
   212  	case invalidShares:
   213  		r1 = mrand.Intn(dealers + 1)      // dealers with invalid shares and will get disqualified
   214  		r2 = mrand.Intn(dealers - r1 + 1) // dealers with invalid shares but will recover
   215  		h = r1
   216  
   217  		var i int
   218  		for i = 0; i < r1; i++ {
   219  			processors[i].malicious = manyInvalidShares
   220  		}
   221  		for ; i < r1+r2; i++ {
   222  			processors[i].malicious = fewInvalidShares
   223  		}
   224  		t.Logf("%d participants will be disqualified, %d other participants will recover\n", r1, r2)
   225  
   226  	case invalidVector:
   227  		r1 = 1 + mrand.Intn(dealers) // dealers with invalid vector and will get disqualified
   228  		h = r1
   229  
   230  		// in this case r2 = 0
   231  		for i := 0; i < r1; i++ {
   232  			processors[i].malicious = invalidVectorBroadcast
   233  		}
   234  		t.Logf("%d participants will be disqualified\n", r1)
   235  
   236  	case invalidComplaint:
   237  		r1 = 1 + mrand.Intn(dealers-1) // participants with invalid complaints and will get disqualified.
   238  		// r1>= 1 to have at least one malicious dealer, and r1<leadrers-1 to leave space for the trigger dealer below.
   239  		r2 = mrand.Intn(dealers - r1) // participants with timeouted complaints: they are considered qualified by honest participants
   240  		// but their results are invalid
   241  		h = r1 + r2 // r2 shouldn't be verified for protocol correctness
   242  
   243  		for i := 0; i < r1; i++ {
   244  			processors[i].malicious = invalidComplaintBroadcast
   245  		}
   246  		for i := r1; i < r1+r2; i++ {
   247  			processors[i].malicious = timeoutedComplaintBroadcast
   248  		}
   249  		// The participant (r1+r2) will send wrong shares and cause the 0..r1+r2-1 dealers to send complaints.
   250  		// This participant doesn't risk getting disqualified as the complaints against them
   251  		// are invalid and won't count. The participant doesn't even answer the complaint.
   252  		processors[r1+r2].malicious = invalidSharesComplainTrigger
   253  		t.Logf("%d participants will be disqualified, %d other participants won't be disqualified.\n", r1, r2)
   254  
   255  	case invalidComplaintAnswer:
   256  		r1 = 1 + mrand.Intn(dealers-1) // participants with invalid complaint answers and will get disqualified.
   257  		// r1>= 1 to have at least one malicious dealer, and r1<leadrers-1 to leave space for the complaint sender.
   258  		h = r1
   259  		// the 0..r1-1 dealers will send invalid shares to n-1 to trigger complaints.
   260  		for i := 0; i < r1; i++ {
   261  			processors[i].malicious = invalidComplaintAnswerBroadcast
   262  		}
   263  		t.Logf("%d participants will be disqualified\n", r1)
   264  	case duplicatedMessages:
   265  		// r1 = r2 = 0
   266  		// participant 0 will send duplicated shares, verif vector and complaint to all participants
   267  		processors[0].malicious = duplicatedSendAndBroadcast
   268  		// participant 1 is a complaint trigger, it sents a wrong share to 0 to trigger a complaint.
   269  		// it also sends duplicated complaint answers.
   270  		processors[1].malicious = invalidSharesComplainTrigger
   271  
   272  	default:
   273  		panic("test case not supported")
   274  	}
   275  
   276  	// number of participants to test
   277  	lead := 0
   278  	var sync sync.WaitGroup
   279  
   280  	// create DKG in all participants
   281  	for current := 0; current < n; current++ {
   282  		var err error
   283  		processors[current].dkg, err = newDKG(dkg, n, threshold,
   284  			current, &processors[current], lead)
   285  		require.NoError(t, err)
   286  	}
   287  
   288  	phase := 0
   289  	if dkg == feldmanVSS { // jump to the last phase since there is only one phase for feldmanVSS
   290  		phase = 2
   291  	}
   292  
   293  	// start DKG in all participants
   294  	// start listening on the channels
   295  	seed := make([]byte, SeedMinLenDKG)
   296  	sync.Add(n)
   297  
   298  	log.Info("DKG protocol starts")
   299  
   300  	for current := 0; current < n; current++ {
   301  		processors[current].startSync.Add(1)
   302  		go dkgRunChan(&processors[current], &sync, t, phase)
   303  	}
   304  
   305  	for current := 0; current < n; current++ {
   306  		// start dkg in parallel
   307  		// ( one common PRG is used internally for all instances which causes a race
   308  		//	in generating randoms and leads to non-deterministic keys. If deterministic keys
   309  		//  are required, switch to sequential calls to dkg.Start()  )
   310  		go func(current int) {
   311  			_, err := crand.Read(seed)
   312  			require.NoError(t, err)
   313  			err = processors[current].dkg.Start(seed)
   314  			require.Nil(t, err)
   315  			processors[current].startSync.Done() // avoids reading messages when a dkg instance hasn't started yet
   316  		}(current)
   317  	}
   318  	phase++
   319  
   320  	// sync the two timeouts and start the next phase
   321  	for ; phase <= 2; phase++ {
   322  		sync.Wait()
   323  		// post processing required for timeout edge case tests
   324  		go timeoutPostProcess(processors, t, phase)
   325  		sync.Add(n)
   326  		for current := 0; current < n; current++ {
   327  			go dkgRunChan(&processors[current], &sync, t, phase)
   328  		}
   329  	}
   330  
   331  	// synchronize the main thread to end all DKGs
   332  	sync.Wait()
   333  
   334  	// assertions and results:
   335  
   336  	// check the disqualified list for all non-disqualified participants
   337  	expected := make([]bool, dealers)
   338  	for i := 0; i < r1; i++ {
   339  		expected[i] = true
   340  	}
   341  
   342  	for i := h; i < n; i++ {
   343  		t.Logf("participant %d is not disqualified, its disqualified list is:\n", i)
   344  		t.Log(processors[i].disqualified)
   345  		assert.Equal(t, expected, processors[i].disqualified)
   346  	}
   347  	// check if DKG is successful
   348  	if (dkg == jointFeldman && (r1 > threshold || (n-r1) <= threshold)) ||
   349  		(dkg == feldmanVSSQual && r1 == 1) { // case of a single dealer
   350  		t.Logf("dkg failed, there are %d disqualified participants\n", r1)
   351  		// DKG failed, check for final errors
   352  		for i := r1; i < n; i++ {
   353  			err := processors[i].finalError
   354  			assert.Error(t, err)
   355  			assert.True(t, IsDKGFailureError(err))
   356  		}
   357  	} else {
   358  		t.Logf("dkg succeeded, there are %d disqualified participants\n", r1)
   359  		// DKG has succeeded, check for final errors
   360  		for i := h; i < n; i++ {
   361  			assert.NoError(t, processors[i].finalError)
   362  		}
   363  		// DKG has succeeded, check the final keys
   364  		for i := h; i < n; i++ {
   365  			assert.True(t, processors[h].pk.Equals(processors[i].pk),
   366  				"2 group public keys are mismatching")
   367  		}
   368  	}
   369  
   370  }
   371  
   372  // time after which a silent channel causes switching to the next dkg phase
   373  const phaseSwitchTimeout = 200 * time.Millisecond
   374  
   375  // This is a testing function
   376  // It simulates processing incoming messages by a participant
   377  // it assumes proc.dkg is already running
   378  func dkgRunChan(proc *testDKGProcessor,
   379  	sync *sync.WaitGroup, t *testing.T, phase int) {
   380  	for {
   381  		select {
   382  		// if a message is received, handle it
   383  		case newMsg := <-proc.chans[proc.current]:
   384  			proc.startSync.Wait() // avoids reading a message when the receiving dkg instance
   385  			// hasn't started yet.
   386  			if newMsg.channel == private {
   387  				err := proc.dkg.HandlePrivateMsg(newMsg.orig, newMsg.data)
   388  				require.Nil(t, err)
   389  			} else {
   390  				err := proc.dkg.HandleBroadcastMsg(newMsg.orig, newMsg.data)
   391  				require.Nil(t, err)
   392  			}
   393  		// if no message is received by the channel, call the DKG timeout
   394  		case <-time.After(phaseSwitchTimeout):
   395  			proc.startSync.Wait() // avoids racing when starting isn't over yet
   396  			switch phase {
   397  			case 0:
   398  				log.Infof("%d shares phase ended\n", proc.current)
   399  				err := proc.dkg.NextTimeout()
   400  				require.Nil(t, err)
   401  			case 1:
   402  				log.Infof("%d complaints phase ended \n", proc.current)
   403  				err := proc.dkg.NextTimeout()
   404  				require.Nil(t, err)
   405  			case 2:
   406  				log.Infof("%d dkg ended \n", proc.current)
   407  				_, pk, _, err := proc.dkg.End()
   408  				proc.finalError = err
   409  				proc.pk = pk
   410  			}
   411  			sync.Done()
   412  			return
   413  		}
   414  	}
   415  }
   416  
   417  // post processing required for some edge case tests
   418  func timeoutPostProcess(processors []testDKGProcessor, t *testing.T, phase int) {
   419  	switch phase {
   420  	case 1:
   421  		for i := 0; i < len(processors); i++ {
   422  			go func(i int) {
   423  				for len(processors[0].lateChansTimeout1[i]) != 0 {
   424  					// to test timeouted messages, late messages are copied to the main channels
   425  					msg := <-processors[0].lateChansTimeout1[i]
   426  					processors[0].chans[i] <- msg
   427  				}
   428  			}(i)
   429  		}
   430  	case 2:
   431  		for i := 0; i < len(processors); i++ {
   432  			go func(i int) {
   433  				for len(processors[0].lateChansTimeout2[i]) != 0 {
   434  					// to test timeouted messages, late messages are copied to the main channels
   435  					msg := <-processors[0].lateChansTimeout2[i]
   436  					processors[0].chans[i] <- msg
   437  				}
   438  			}(i)
   439  		}
   440  	}
   441  }
   442  
   443  // implements DKGProcessor interface
   444  type testDKGProcessor struct {
   445  	// instnce of DKG
   446  	dkg DKGState
   447  	// index of the current participant in the protocol
   448  	current int
   449  	// group public key, output of DKG
   450  	pk PublicKey
   451  	// final disqualified list
   452  	disqualified []bool
   453  	// final output error of the DKG
   454  	finalError error
   455  	// type of malicious behavior
   456  	malicious behavior
   457  	// start DKG syncer
   458  	startSync sync.WaitGroup
   459  
   460  	// main message channels
   461  	chans []chan *message
   462  	// extra channels for late messges with regards to the first timeout, and second timeout
   463  	lateChansTimeout1 []chan *message
   464  	lateChansTimeout2 []chan *message
   465  	// type of the protocol
   466  	protocol int
   467  
   468  	// only used when testing the threshold signature stateful api
   469  	ts   *blsThresholdSignatureParticipant
   470  	keys *statelessKeys
   471  }
   472  
   473  const (
   474  	dkgType int = iota
   475  	tsType
   476  )
   477  
   478  const (
   479  	broadcast int = iota
   480  	private
   481  )
   482  
   483  type message struct {
   484  	orig     int
   485  	protocol int
   486  	channel  int
   487  	data     []byte
   488  }
   489  
   490  func (proc *testDKGProcessor) Disqualify(participant int, logInfo string) {
   491  	gt.Logf("%d disqualifies %d, %s\n", proc.current, participant, logInfo)
   492  	proc.disqualified[participant] = true
   493  }
   494  
   495  func (proc *testDKGProcessor) FlagMisbehavior(participant int, logInfo string) {
   496  	gt.Logf("%d flags a misbehavior from %d: %s", proc.current, participant, logInfo)
   497  }
   498  
   499  // This is a testing function
   500  // it simulates sending a message from one participant to another
   501  func (proc *testDKGProcessor) PrivateSend(dest int, data []byte) {
   502  	go func() {
   503  		log.Infof("%d sending to %d", proc.current, dest)
   504  		if proc.malicious == fewInvalidShares || proc.malicious == manyInvalidShares ||
   505  			proc.malicious == invalidSharesComplainTrigger || proc.malicious == invalidComplaintAnswerBroadcast ||
   506  			proc.malicious == duplicatedSendAndBroadcast {
   507  			proc.invalidShareSend(dest, data)
   508  			return
   509  		}
   510  		proc.honestSend(dest, data)
   511  	}()
   512  }
   513  
   514  // This is a testing function
   515  // it simulates sending a honest message from one participant to another
   516  func (proc *testDKGProcessor) honestSend(dest int, data []byte) {
   517  	gt.Logf("%d honestly sending to %d:\n%x\n", proc.current, dest, data)
   518  	newMsg := &message{proc.current, proc.protocol, private, data}
   519  	proc.chans[dest] <- newMsg
   520  }
   521  
   522  // This is a testing function
   523  // it simulates sending a malicious message from one participant to another
   524  // This function simulates the behavior of a malicious participant.
   525  func (proc *testDKGProcessor) invalidShareSend(dest int, data []byte) {
   526  
   527  	// check the behavior
   528  	var recipients int // number of recipients to send invalid shares to
   529  	switch proc.malicious {
   530  	case manyInvalidShares:
   531  		recipients = proc.dkg.Threshold() + 1 //  t < recipients <= n
   532  	case fewInvalidShares:
   533  		recipients = proc.dkg.Threshold() //  0 <= recipients <= t
   534  	case invalidSharesComplainTrigger:
   535  		recipients = proc.current // equal to r1+r2, which causes all r1+r2 to complain
   536  	case invalidComplaintAnswerBroadcast:
   537  		recipients = 0 // treat this case separately as the complaint trigger is the participant n-1
   538  	case duplicatedSendAndBroadcast:
   539  		proc.honestSend(dest, data)
   540  		proc.honestSend(dest, data)
   541  		return
   542  	default:
   543  		panic("invalid share send not supported")
   544  	}
   545  
   546  	// copy of data
   547  	newData := make([]byte, len(data))
   548  	copy(newData, data)
   549  
   550  	newMsg := &message{proc.current, proc.protocol, private, newData}
   551  	originalMsg := &message{proc.current, proc.protocol, private, data}
   552  
   553  	// check destination
   554  	if (dest < recipients) || (proc.current < recipients && dest < recipients+1) ||
   555  		(proc.malicious == invalidComplaintAnswerBroadcast && dest == proc.dkg.Size()-1) {
   556  		// choose a random reason for an invalid share
   557  		coin := mrand.Intn(7)
   558  		gt.Logf("%d maliciously sending to %d, coin is %d\n", proc.current, dest, coin)
   559  		switch coin {
   560  		case 0:
   561  			// value doesn't match the verification vector
   562  			newMsg.data[8]++
   563  			proc.chans[dest] <- newMsg
   564  		case 1:
   565  			// empty message
   566  			newMsg.data = newMsg.data[:0]
   567  			proc.chans[dest] <- newMsg
   568  		case 2:
   569  			// valid message length but invalid share length
   570  			newMsg.data = newMsg.data[:1]
   571  			proc.chans[dest] <- newMsg
   572  		case 3:
   573  			// invalid value
   574  			for i := 0; i < len(newMsg.data); i++ {
   575  				newMsg.data[i] = 0xFF
   576  			}
   577  			proc.chans[dest] <- newMsg
   578  		case 4:
   579  			// do not send the share at all
   580  			return
   581  		case 5:
   582  			// wrong header: will cause a complaint
   583  			newMsg.data[0] = byte(feldmanVSSVerifVec)
   584  			proc.chans[dest] <- newMsg
   585  		case 6:
   586  			// message will be sent after the shares timeout and will be considered late
   587  			// by the receiver. All late messages go into a separate channel and will be sent to
   588  			// the main channel after the shares timeout.
   589  			proc.lateChansTimeout1[dest] <- newMsg
   590  			return
   591  		}
   592  
   593  	} else {
   594  		gt.Logf("turns out to be a honest send\n%x\n", data)
   595  	}
   596  	// honest send case: this is the only message sent
   597  	// malicious send case: this is a second correct send, to test the second message gets ignored
   598  	// by the receiver (sender has been tagged malicious after the first send)
   599  	proc.chans[dest] <- originalMsg
   600  
   601  }
   602  
   603  // This is a testing function
   604  // it simulates broadcasting a message from one participant to all participants
   605  func (proc *testDKGProcessor) Broadcast(data []byte) {
   606  	go func() {
   607  		log.Infof("%d Broadcasting:", proc.current)
   608  
   609  		if data[0] == byte(feldmanVSSVerifVec) && proc.malicious == invalidVectorBroadcast {
   610  			proc.invalidVectorBroadcast(data)
   611  		} else if data[0] == byte(feldmanVSSComplaint) &&
   612  			(proc.malicious == invalidComplaintBroadcast || proc.malicious == timeoutedComplaintBroadcast) {
   613  			proc.invalidComplaintBroadcast(data)
   614  		} else if data[0] == byte(feldmanVSSComplaintAnswer) && proc.malicious == invalidComplaintAnswerBroadcast {
   615  			proc.invalidComplaintAnswerBroadcast(data)
   616  		} else if proc.malicious == duplicatedSendAndBroadcast ||
   617  			(data[0] == byte(feldmanVSSComplaintAnswer) && proc.malicious == invalidSharesComplainTrigger) {
   618  			// the complaint trigger also sends duplicated complaint answers
   619  			proc.honestBroadcast(data)
   620  			proc.honestBroadcast(data)
   621  		} else {
   622  			proc.honestBroadcast(data)
   623  		}
   624  	}()
   625  }
   626  
   627  func (proc *testDKGProcessor) honestBroadcast(data []byte) {
   628  	gt.Logf("%d honestly broadcasting:\n%x\n", proc.current, data)
   629  	newMsg := &message{proc.current, proc.protocol, broadcast, data}
   630  	for i := 0; i < len(proc.chans); i++ {
   631  		if i != proc.current {
   632  			proc.chans[i] <- newMsg
   633  		}
   634  	}
   635  }
   636  
   637  func (proc *testDKGProcessor) invalidVectorBroadcast(data []byte) {
   638  	newMsg := &message{proc.current, proc.protocol, broadcast, data}
   639  
   640  	// choose a random reason of an invalid vector
   641  	coin := mrand.Intn(5)
   642  	gt.Logf("%d malicious vector broadcast, coin is %d\n", proc.current, coin)
   643  	switch coin {
   644  	case 0:
   645  		// invalid point serialization
   646  		newMsg.data[1] = 0xFF
   647  	case 1:
   648  		// invalid length
   649  		newMsg.data = newMsg.data[:5]
   650  	case 2:
   651  		// do not send the vector at all
   652  		return
   653  	case 3:
   654  		// wrong header
   655  		newMsg.data[0] = byte(feldmanVSSShare)
   656  	case 4:
   657  		// send the vector after the first timeout, equivalent to not sending at all
   658  		// as the vector should be ignored.
   659  		for i := 0; i < proc.dkg.Size(); i++ {
   660  			if i != proc.current {
   661  				proc.lateChansTimeout1[i] <- newMsg
   662  			}
   663  		}
   664  		return
   665  	}
   666  	gt.Logf("%x\n", newMsg.data)
   667  	for i := 0; i < proc.dkg.Size(); i++ {
   668  		if i != proc.current {
   669  			proc.chans[i] <- newMsg
   670  		}
   671  	}
   672  }
   673  
   674  func (proc *testDKGProcessor) invalidComplaintBroadcast(data []byte) {
   675  	newMsg := &message{proc.current, proc.protocol, broadcast, data}
   676  
   677  	if proc.malicious == invalidComplaintBroadcast {
   678  
   679  		// choose a random reason for an invalid complaint
   680  		coin := mrand.Intn(2)
   681  		gt.Logf("%d malicious complaint broadcast, coin is %d\n", proc.current, coin)
   682  		switch coin {
   683  		case 0:
   684  			// invalid complainee
   685  			newMsg.data[1] = byte(proc.dkg.Size() + 1)
   686  		case 1:
   687  			// invalid length
   688  			newMsg.data = make([]byte, complaintSize+5)
   689  			copy(newMsg.data, data)
   690  		}
   691  		gt.Logf("%x\n", newMsg.data)
   692  		for i := 0; i < len(proc.chans); i++ {
   693  			if i != proc.current {
   694  				proc.chans[i] <- newMsg
   695  			}
   696  		}
   697  	} else if proc.malicious == timeoutedComplaintBroadcast {
   698  		gt.Logf("%d timeouted complaint broadcast\n", proc.current)
   699  		// send the complaint after the second timeout, equivalent to not sending at all
   700  		// as the complaint should be ignored.
   701  		for i := 0; i < len(proc.chans); i++ {
   702  			if i != proc.current {
   703  				proc.lateChansTimeout2[i] <- newMsg
   704  			}
   705  		}
   706  		return
   707  	}
   708  }
   709  
   710  func (proc *testDKGProcessor) invalidComplaintAnswerBroadcast(data []byte) {
   711  	newMsg := &message{proc.current, proc.protocol, broadcast, data}
   712  
   713  	// choose a random reason for an invalid complaint
   714  	coin := mrand.Intn(3)
   715  	gt.Logf("%d malicious complaint answer broadcast, coin is %d\n", proc.current, coin)
   716  	switch coin {
   717  	case 0:
   718  		// invalid complainee
   719  		newMsg.data[1] = byte(proc.dkg.Size() + 1)
   720  	case 1:
   721  		// invalid length
   722  		newMsg.data = make([]byte, complaintAnswerSize+5)
   723  		copy(newMsg.data, data)
   724  	case 2:
   725  		// no answer at all
   726  		return
   727  	}
   728  	//gt.Logf("%x\n", newMsg.data)
   729  	for i := 0; i < len(proc.chans); i++ {
   730  		if i != proc.current {
   731  			proc.chans[i] <- newMsg
   732  		}
   733  	}
   734  }
   735  
   736  // implements a dummy DKGProcessor
   737  type dummyTestDKGProcessor struct {
   738  }
   739  
   740  func (proc dummyTestDKGProcessor) PrivateSend(int, []byte)     {}
   741  func (proc dummyTestDKGProcessor) Broadcast([]byte)            {}
   742  func (proc dummyTestDKGProcessor) Disqualify(int, string)      {}
   743  func (proc dummyTestDKGProcessor) FlagMisbehavior(int, string) {}
   744  
   745  func TestDKGErrorTypes(t *testing.T) {
   746  	t.Run("dkgFailureError sanity", func(t *testing.T) {
   747  		failureError := dkgFailureErrorf("some error")
   748  		invInpError := invalidInputsErrorf("")
   749  		otherError := fmt.Errorf("some error")
   750  		assert.True(t, IsDKGFailureError(failureError))
   751  		assert.False(t, IsDKGFailureError(otherError))
   752  		assert.False(t, IsDKGFailureError(invInpError))
   753  		assert.False(t, IsDKGFailureError(nil))
   754  		assert.False(t, IsInvalidInputsError(failureError))
   755  	})
   756  
   757  	t.Run("dkgInvalidStateTransitionError sanity", func(t *testing.T) {
   758  		failureError := dkgInvalidStateTransitionErrorf("some error")
   759  		invInpError := invalidInputsErrorf("")
   760  		otherError := fmt.Errorf("some error")
   761  		assert.True(t, IsDKGInvalidStateTransitionError(failureError))
   762  		assert.False(t, IsInvalidInputsError(failureError))
   763  		assert.False(t, IsDKGInvalidStateTransitionError(invInpError))
   764  		assert.False(t, IsDKGInvalidStateTransitionError(otherError))
   765  		assert.False(t, IsDKGInvalidStateTransitionError(nil))
   766  	})
   767  }
   768  
   769  func TestDKGTransitionErrors(t *testing.T) {
   770  	n := 5
   771  	threshold := 3
   772  	myIndex := 0
   773  	dealer := 1
   774  	seed := make([]byte, SeedMinLenDKG)
   775  
   776  	t.Run("feldman VSS", func(t *testing.T) {
   777  		state, err := NewFeldmanVSS(n, threshold, myIndex, dummyTestDKGProcessor{}, dealer)
   778  		require.NoError(t, err)
   779  		// calls before start
   780  		err = state.ForceDisqualify(1)
   781  		assert.True(t, IsDKGInvalidStateTransitionError(err))
   782  		err = state.HandlePrivateMsg(1, []byte{})
   783  		assert.True(t, IsDKGInvalidStateTransitionError(err))
   784  		err = state.HandleBroadcastMsg(1, []byte{})
   785  		assert.True(t, IsDKGInvalidStateTransitionError(err))
   786  		_, _, _, err = state.End()
   787  		assert.True(t, IsDKGInvalidStateTransitionError(err))
   788  	})
   789  
   790  	t.Run("Feldman VSS Qualif and joint-Feldman ", func(t *testing.T) {
   791  		stateFVSSQ, err := NewFeldmanVSSQual(n, threshold, myIndex, dummyTestDKGProcessor{}, dealer)
   792  		require.NoError(t, err)
   793  		stateJF, err := NewJointFeldman(n, threshold, myIndex, dummyTestDKGProcessor{})
   794  		require.NoError(t, err)
   795  
   796  		for _, state := range []DKGState{stateFVSSQ, stateJF} {
   797  			// calls before start
   798  			err = state.ForceDisqualify(1)
   799  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   800  			err = state.HandlePrivateMsg(1, []byte{})
   801  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   802  			err = state.HandleBroadcastMsg(1, []byte{})
   803  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   804  			_, _, _, err = state.End()
   805  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   806  			err = state.NextTimeout()
   807  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   808  			// after start
   809  			err = state.Start(seed)
   810  			require.NoError(t, err)
   811  			_, _, _, err = state.End()
   812  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   813  			// after first timeout
   814  			err = state.NextTimeout()
   815  			require.NoError(t, err)
   816  			err = state.Start(seed)
   817  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   818  			_, _, _, err = state.End()
   819  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   820  			// after second timeout
   821  			err = state.NextTimeout()
   822  			require.NoError(t, err)
   823  			err = state.Start(seed)
   824  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   825  			err = state.NextTimeout()
   826  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   827  			// after end
   828  			_, _, _, err = state.End()
   829  			require.True(t, IsDKGFailureError(err))
   830  			err = state.NextTimeout()
   831  			assert.True(t, IsDKGInvalidStateTransitionError(err))
   832  		}
   833  	})
   834  }