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

     1  //go:build relic
     2  // +build relic
     3  
     4  package crypto
     5  
     6  // #cgo CFLAGS: -g -Wall -std=c99
     7  // #include "dkg_include.h"
     8  import "C"
     9  
    10  import (
    11  	"fmt"
    12  )
    13  
    14  // Implements Feldman Verifiable Secret Sharing using
    15  // the BLS set up on the BLS12-381 curve. A complaint mechanism
    16  // is added to qualify/disqualify the dealer if they misbehave.
    17  
    18  // The secret is a BLS private key generated by the dealer.
    19  // (and hence this is a centralized generation).
    20  // The dealer generates key shares for a BLS-based
    21  // threshold signature scheme and distributes the shares over the (n)
    22  // participants including itself. The participants validate their shares
    23  // using a public verification vector shared by the dealer and are able
    24  // to broadcast complaints against a misbehaving dealer.
    25  
    26  // The dealer has the chance to avoid being disqualified by broadcasting
    27  // a complaint answer. The protocol ends with all honest participants
    28  // reaching a consensus about the dealer qualification/disqualification.
    29  
    30  // Private keys are scalar in Zr, where r is the group order of G1/G2
    31  // Public keys are in G2.
    32  
    33  // feldman VSS protocol, with complaint mechanism, implements DKGState
    34  type feldmanVSSQualState struct {
    35  	// feldmanVSSstate state
    36  	*feldmanVSSstate
    37  	// complaints received against the dealer:
    38  	// the key is the origin of the complaint
    39  	// a complaint will be created if a complaint message or an answer was
    40  	// broadcasted, a complaint will be checked only when both the
    41  	// complaint message and the answer were broadcasted
    42  	complaints map[index]*complaint
    43  	// is the dealer disqualified
    44  	disqualified bool
    45  	// Timeout to receive shares and verification vector
    46  	// - if a share is not received before this timeout a complaint will be formed
    47  	// - if the verification is not received before this timeout,
    48  	// dealer is disqualified
    49  	sharesTimeout bool
    50  	// Timeout to receive complaints
    51  	// all complaints received after this timeout are ignored
    52  	complaintsTimeout bool
    53  }
    54  
    55  // these data are required to justify a slashing
    56  type complaint struct {
    57  	received       bool
    58  	answerReceived bool
    59  	answer         scalar
    60  }
    61  
    62  // NewFeldmanVSSQual creates a new instance of a Feldman VSS protocol
    63  // with a qualification mechanism.
    64  //
    65  // An instance is run by a single participant and is usable for only one protocol.
    66  // In order to run the protocol again, a new instance needs to be created
    67  //
    68  // The function returns:
    69  //   - (nil, InvalidInputsError) if:
    70  //   - size if not in [DKGMinSize, DKGMaxSize]
    71  //   - threshold is not in [MinimumThreshold, size-1]
    72  //   - myIndex is not in [0, size-1]
    73  //   - dealerIndex is not in [0, size-1]
    74  //   - (dkgInstance, nil) otherwise
    75  func NewFeldmanVSSQual(size int, threshold int, myIndex int,
    76  	processor DKGProcessor, dealerIndex int) (DKGState, error) {
    77  
    78  	common, err := newDKGCommon(size, threshold, myIndex, processor, dealerIndex)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	fvss := &feldmanVSSstate{
    84  		dkgCommon:   common,
    85  		dealerIndex: index(dealerIndex),
    86  	}
    87  	fvssq := &feldmanVSSQualState{
    88  		feldmanVSSstate: fvss,
    89  		disqualified:    false,
    90  	}
    91  	fvssq.init()
    92  	return fvssq, nil
    93  }
    94  
    95  func (s *feldmanVSSQualState) init() {
    96  	s.feldmanVSSstate.init()
    97  	s.complaints = make(map[index]*complaint)
    98  }
    99  
   100  // NextTimeout sets the next protocol timeout
   101  // This function needs to be called twice by every participant in
   102  // the Feldman VSS Qual protocol.
   103  // The first call is a timeout for sharing the private shares.
   104  // The second call is a timeout for broadcasting the complaints.
   105  //
   106  // The returned erorr is :
   107  //   - dkgInvalidStateTransitionError if the DKG instance was not running.
   108  //   - dkgInvalidStateTransitionError if the DKG instance already called the 2 required timeouts.
   109  //   - nil otherwise.
   110  func (s *feldmanVSSQualState) NextTimeout() error {
   111  	if !s.running {
   112  		return dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
   113  	}
   114  	if s.complaintsTimeout {
   115  		return dkgInvalidStateTransitionErrorf("the next timeout should be to end DKG protocol")
   116  	}
   117  
   118  	// if dealer is already disqualified, there is nothing to do
   119  	if s.disqualified {
   120  		if !s.sharesTimeout {
   121  			s.sharesTimeout = true
   122  			return nil
   123  		} else {
   124  			s.complaintsTimeout = true
   125  			return nil
   126  		}
   127  	}
   128  
   129  	if !s.sharesTimeout {
   130  		s.setSharesTimeout()
   131  		return nil
   132  	} else {
   133  		s.setComplaintsTimeout()
   134  		return nil
   135  	}
   136  }
   137  
   138  // End ends the protocol in the current participant.
   139  // This is also a timeout to receiving all complaint answers.
   140  // It returns the finalized public data and participant private key share:
   141  //  1. the group public key corresponding to the group secret key
   142  //  2. all the public key shares corresponding to the participants private key shares.
   143  //  3. the finalized private key which is the current participant's own private key share
   144  //  4. Error Returns:
   145  //     - dkgFailureError if the dealer was disqualified.
   146  //     - dkgFailureError if the public key share or group public key is identity.
   147  //     - dkgInvalidStateTransition if Start() was not called, or NextTimeout() was not called twice
   148  //     - nil otherwise.
   149  func (s *feldmanVSSQualState) End() (PrivateKey, PublicKey, []PublicKey, error) {
   150  	if !s.running {
   151  		return nil, nil, nil, dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
   152  	}
   153  	if !s.sharesTimeout || !s.complaintsTimeout {
   154  		return nil, nil, nil,
   155  			dkgInvalidStateTransitionErrorf("%d: two timeouts should be set before ending dkg", s.myIndex)
   156  	}
   157  	s.running = false
   158  	// check if a complaint has remained without an answer
   159  	// a dealer is disqualified if a complaint was never answered
   160  	if !s.disqualified {
   161  		for complainer, c := range s.complaints {
   162  			if c.received && !c.answerReceived {
   163  				s.disqualified = true
   164  				s.processor.Disqualify(int(s.dealerIndex),
   165  					fmt.Sprintf("complaint from %d was not answered",
   166  						complainer))
   167  				break
   168  			}
   169  		}
   170  	}
   171  
   172  	// If the dealer is disqualified, all keys are ignored
   173  	// otherwise, the keys are valid
   174  	if s.disqualified {
   175  		return nil, nil, nil, dkgFailureErrorf("dealer is disqualified")
   176  	}
   177  
   178  	// private key of the current participant
   179  	x := newPrKeyBLSBLS12381(&s.x)
   180  
   181  	// Group public key
   182  	Y := newPubKeyBLSBLS12381(&s.vA[0])
   183  
   184  	// The participants public keys
   185  	y := make([]PublicKey, s.size)
   186  	for i, p := range s.y {
   187  		y[i] = newPubKeyBLSBLS12381(&p)
   188  	}
   189  
   190  	// check if current public key share or group public key is identity.
   191  	// In that case all signatures generated by the key are invalid (as stated by the BLS IETF
   192  	//	draft) to avoid equivocation issues.
   193  	// TODO: update generateShares to make sure no public key share is identity AND
   194  	// update receiveVector function to disqualify the dealer if any public key share
   195  	// is identity, only when FeldmanVSSQ is not a building primitive of Joint-Feldman
   196  	if (&s.x).isZero() {
   197  		s.disqualified = true
   198  		return nil, nil, nil, dkgFailureErrorf("private key share is identity and therefore invalid")
   199  	}
   200  	if Y.isIdentity {
   201  		s.disqualified = true
   202  		return nil, nil, nil, dkgFailureErrorf("group private key is identity and is therefore invalid")
   203  	}
   204  	return x, Y, y, nil
   205  }
   206  
   207  const (
   208  	complaintSize       = 1
   209  	complaintAnswerSize = 1 + PrKeyLenBLSBLS12381
   210  )
   211  
   212  // HandleBroadcastMsg processes a new broadcasted message received by the current participant.
   213  // orig is the message origin index
   214  //
   215  // The function returns:
   216  //   - dkgInvalidStateTransitionError if the instance is not running
   217  //   - invalidInputsError if `orig` is not valid (in [0, size-1])
   218  //   - nil otherwise
   219  func (s *feldmanVSSQualState) HandleBroadcastMsg(orig int, msg []byte) error {
   220  	if !s.running {
   221  		return dkgInvalidStateTransitionErrorf("dkg is not running")
   222  	}
   223  
   224  	if orig >= s.Size() || orig < 0 {
   225  		return invalidInputsErrorf(
   226  			"wrong origin input, should be less than %d, got %d",
   227  			s.Size(),
   228  			orig)
   229  	}
   230  
   231  	// In case a message is received by the origin participant,
   232  	// the message is just ignored
   233  	if s.myIndex == index(orig) {
   234  		return nil
   235  	}
   236  
   237  	// if dealer is already disqualified, ignore the message
   238  	if s.disqualified {
   239  		return nil
   240  	}
   241  
   242  	if len(msg) == 0 {
   243  		if index(orig) == s.dealerIndex {
   244  			s.disqualified = true
   245  		}
   246  		s.processor.Disqualify(orig, "received broadcast is empty")
   247  		return nil
   248  	}
   249  
   250  	switch dkgMsgTag(msg[0]) {
   251  	case feldmanVSSVerifVec:
   252  		s.receiveVerifVector(index(orig), msg[1:])
   253  	case feldmanVSSComplaint:
   254  		s.receiveComplaint(index(orig), msg[1:])
   255  	case feldmanVSSComplaintAnswer:
   256  		s.receiveComplaintAnswer(index(orig), msg[1:])
   257  	default:
   258  		if index(orig) == s.dealerIndex {
   259  			s.disqualified = true
   260  		}
   261  		s.processor.Disqualify(orig,
   262  			fmt.Sprintf("invalid broadcast header, got %d",
   263  				dkgMsgTag(msg[0])))
   264  	}
   265  	return nil
   266  }
   267  
   268  // HandlePrivateMsg processes a new private message received by the current participant.
   269  // orig is the message origin index.
   270  //
   271  // The function returns:
   272  //   - dkgInvalidStateTransitionError if the instance is not running
   273  //   - invalidInputsError if `orig` is not valid (in [0, size-1])
   274  //   - nil otherwise
   275  func (s *feldmanVSSQualState) HandlePrivateMsg(orig int, msg []byte) error {
   276  	if !s.running {
   277  		return dkgInvalidStateTransitionErrorf("dkg is not running")
   278  	}
   279  	if orig >= s.Size() || orig < 0 {
   280  		return invalidInputsErrorf(
   281  			"invalid origin, should be positive less than %d, got %d",
   282  			s.Size(),
   283  			orig)
   284  	}
   285  
   286  	// In case a private message is received by the origin participant,
   287  	// the message is just ignored
   288  	if s.myIndex == index(orig) {
   289  		return nil
   290  	}
   291  
   292  	// if dealer is already disqualified, ignore the message
   293  	if s.disqualified {
   294  		return nil
   295  	}
   296  
   297  	// forward all the message to receiveShare because any private message
   298  	// has to be a private share
   299  	s.receiveShare(index(orig), msg)
   300  
   301  	return nil
   302  }
   303  
   304  // ForceDisqualify forces a participant to get disqualified
   305  // for a reason outside of the DKG protocol
   306  // The caller should make sure all honest participants call this function,
   307  // otherwise, the protocol can be broken
   308  //
   309  // The function returns:
   310  //   - dkgInvalidStateTransitionError if the instance is not running
   311  //   - invalidInputsError if `orig` is not valid (in [0, size-1])
   312  //   - nil otherwise
   313  func (s *feldmanVSSQualState) ForceDisqualify(participant int) error {
   314  	if !s.running {
   315  		return dkgInvalidStateTransitionErrorf("dkg is not running")
   316  	}
   317  	if participant >= s.Size() || participant < 0 {
   318  		return invalidInputsErrorf(
   319  			"invalid origin input, should be less than %d, got %d",
   320  			s.Size(), participant)
   321  	}
   322  	if index(participant) == s.dealerIndex {
   323  		s.disqualified = true
   324  	}
   325  	return nil
   326  }
   327  
   328  // The function does not check the call respects the machine
   329  // state transition of feldmanVSSQual. The calling function must make sure this call
   330  // is valid.
   331  func (s *feldmanVSSQualState) setSharesTimeout() {
   332  	s.sharesTimeout = true
   333  	// if verif vector is not received, disqualify the dealer
   334  	if !s.vAReceived {
   335  		s.disqualified = true
   336  		s.processor.Disqualify(int(s.dealerIndex),
   337  			"verification vector was not received")
   338  		return
   339  	}
   340  	// if share is not received, make a complaint
   341  	if !s.xReceived {
   342  		s.buildAndBroadcastComplaint()
   343  	}
   344  }
   345  
   346  // The function does not check the call respects the machine
   347  // state transition of feldmanVSSQual. The calling function must make sure this call
   348  // is valid.
   349  func (s *feldmanVSSQualState) setComplaintsTimeout() {
   350  	s.complaintsTimeout = true
   351  	// if more than t complaints are received, the dealer is disqualified
   352  	// regardless of the answers.
   353  	// (at this point, all answered complaints should have been already received)
   354  	// (i.e there is no complaint with (!c.received && c.answerReceived)
   355  	if len(s.complaints) > s.threshold {
   356  		s.disqualified = true
   357  		s.processor.Disqualify(int(s.dealerIndex),
   358  			fmt.Sprintf("there are %d complaints, they exceeded the threshold %d",
   359  				len(s.complaints), s.threshold))
   360  	}
   361  }
   362  
   363  func (s *feldmanVSSQualState) receiveShare(origin index, data []byte) {
   364  	// only accept private shares from the dealer.
   365  	if origin != s.dealerIndex {
   366  		return
   367  	}
   368  
   369  	// check the share timeout
   370  	if s.sharesTimeout {
   371  		s.processor.FlagMisbehavior(int(origin),
   372  			"private share is received after the shares timeout")
   373  		return
   374  	}
   375  
   376  	if s.xReceived {
   377  		s.processor.FlagMisbehavior(int(origin),
   378  			"private share was already received")
   379  		return
   380  	}
   381  
   382  	// at this point, tag private share is received
   383  	s.xReceived = true
   384  
   385  	// private message general check
   386  	if len(data) == 0 || dkgMsgTag(data[0]) != feldmanVSSShare {
   387  		s.buildAndBroadcastComplaint()
   388  		s.processor.FlagMisbehavior(int(origin),
   389  			fmt.Sprintf("private share should be non-empty and first byte should be %d, received %#x",
   390  				feldmanVSSShare, data))
   391  		return
   392  	}
   393  
   394  	// consider the remaining data from message
   395  	data = data[1:]
   396  
   397  	if (len(data)) != shareSize {
   398  		s.buildAndBroadcastComplaint()
   399  		s.processor.FlagMisbehavior(int(origin),
   400  			fmt.Sprintf("invalid share size, expects %d, got %d",
   401  				shareSize, len(data)))
   402  		return
   403  	}
   404  	// read the participant private share
   405  	if C.bn_read_Zr_bin((*C.bn_st)(&s.x),
   406  		(*C.uchar)(&data[0]),
   407  		PrKeyLenBLSBLS12381,
   408  	) != valid {
   409  		s.buildAndBroadcastComplaint()
   410  		s.processor.FlagMisbehavior(int(origin),
   411  			fmt.Sprintf("invalid share value %x", data))
   412  		return
   413  	}
   414  
   415  	if s.vAReceived {
   416  		if !s.verifyShare() {
   417  			// otherwise, build a complaint
   418  			s.buildAndBroadcastComplaint()
   419  		}
   420  	}
   421  }
   422  
   423  func (s *feldmanVSSQualState) receiveVerifVector(origin index, data []byte) {
   424  	// only accept the verification vector from the dealer.
   425  	if origin != s.dealerIndex {
   426  		return
   427  	}
   428  
   429  	// check the share timeout
   430  	if s.sharesTimeout {
   431  		s.processor.FlagMisbehavior(int(origin),
   432  			"verification vector received after the shares timeout")
   433  		return
   434  	}
   435  
   436  	if s.vAReceived {
   437  		s.processor.FlagMisbehavior(int(origin),
   438  			"verification received was already received")
   439  		return
   440  	}
   441  	s.vAReceived = true
   442  
   443  	if len(data) != verifVectorSize*(s.threshold+1) {
   444  		s.disqualified = true
   445  		s.processor.Disqualify(int(origin),
   446  			fmt.Sprintf("invalid verification vector size, expects %d, got %d",
   447  				verifVectorSize*(s.threshold+1), len(data)))
   448  		return
   449  	}
   450  	// read the verification vector
   451  	s.vA = make([]pointG2, s.threshold+1)
   452  	err := readVerifVector(s.vA, data)
   453  	if err != nil {
   454  		s.disqualified = true
   455  		s.processor.Disqualify(int(origin),
   456  			fmt.Sprintf("reading the verification vector failed:%s", err))
   457  		return
   458  	}
   459  
   460  	s.y = make([]pointG2, s.size)
   461  	s.computePublicKeys()
   462  
   463  	// check the (already) registered complaints
   464  	for complainer, c := range s.complaints {
   465  		if c.received && c.answerReceived {
   466  			if s.checkComplaint(complainer, c) {
   467  				s.disqualified = true
   468  				s.processor.Disqualify(int(s.dealerIndex),
   469  					fmt.Sprintf("verification vector received: a complaint answer to %d is invalid",
   470  						complainer))
   471  				return
   472  			}
   473  		}
   474  	}
   475  	// check the private share
   476  	if s.xReceived {
   477  		if !s.verifyShare() {
   478  			s.buildAndBroadcastComplaint()
   479  		}
   480  	}
   481  }
   482  
   483  // build a complaint against the dealer, add it to the local
   484  // complaint map and broadcast it
   485  func (s *feldmanVSSQualState) buildAndBroadcastComplaint() {
   486  	s.complaints[s.myIndex] = &complaint{
   487  		received:       true,
   488  		answerReceived: false,
   489  	}
   490  	data := []byte{byte(feldmanVSSComplaint), byte(s.dealerIndex)}
   491  	s.processor.Broadcast(data)
   492  }
   493  
   494  // build a complaint answer, add it to the local
   495  // complaint map and broadcast it
   496  func (s *feldmanVSSQualState) buildAndBroadcastComplaintAnswer(complainee index) {
   497  	data := make([]byte, complaintAnswerSize+1)
   498  	data[0] = byte(feldmanVSSComplaintAnswer)
   499  	data[1] = byte(complainee)
   500  	zrPolynomialImage(data[2:], s.a, complainee+1, nil)
   501  	s.complaints[complainee].answerReceived = true
   502  	s.processor.Broadcast(data)
   503  }
   504  
   505  // assuming a complaint and its answer were both received, this function returns:
   506  // - false if the complaint answer is correct
   507  // - true if the complaint answer is not correct
   508  func (s *feldmanVSSQualState) checkComplaint(complainer index, c *complaint) bool {
   509  	// check y[complainer] == share.G2
   510  	return C.verifyshare((*C.bn_st)(&c.answer),
   511  		(*C.ep2_st)(&s.y[complainer])) == 0
   512  }
   513  
   514  // data = |complainee|
   515  func (s *feldmanVSSQualState) receiveComplaint(origin index, data []byte) {
   516  	// check the complaint timeout
   517  	if s.complaintsTimeout {
   518  		s.processor.FlagMisbehavior(int(origin),
   519  			"complaint received after the complaint timeout")
   520  		return
   521  	}
   522  
   523  	if len(data) != complaintSize {
   524  		// only the dealer of the instance gets disqualified
   525  		if origin == s.dealerIndex {
   526  			s.disqualified = true
   527  			s.processor.Disqualify(int(origin),
   528  				fmt.Sprintf("invalid complaint size, expects %d, got %d",
   529  					complaintSize, len(data)))
   530  		}
   531  		return
   532  	}
   533  
   534  	// the byte encodes the complainee
   535  	complainee := index(data[0])
   536  
   537  	// validate the complainee value
   538  	if int(complainee) >= s.size {
   539  		// only the dealer of the instance gets disqualified
   540  		if origin == s.dealerIndex {
   541  			s.disqualified = true
   542  			s.processor.Disqualify(int(origin),
   543  				fmt.Sprintf("invalid complainee, should be less than %d, got %d",
   544  					s.size, complainee))
   545  		}
   546  		return
   547  	}
   548  
   549  	// if the complaint is coming from the dealer, ignore it
   550  	if origin == s.dealerIndex {
   551  		return
   552  	}
   553  
   554  	// if the complainee is not the dealer, ignore the complaint
   555  	if complainee != s.dealerIndex {
   556  		return
   557  	}
   558  
   559  	c, ok := s.complaints[origin]
   560  	// if the complaint is new, add it
   561  	if !ok {
   562  		s.complaints[origin] = &complaint{
   563  			received:       true,
   564  			answerReceived: false,
   565  		}
   566  		// if the complainee is the current participant, prepare an answer
   567  		if s.myIndex == s.dealerIndex {
   568  			s.buildAndBroadcastComplaintAnswer(origin)
   569  		}
   570  		return
   571  	}
   572  	// complaint is not new in the map
   573  	// check if the complaint has been already received
   574  	if c.received {
   575  		s.processor.FlagMisbehavior(int(origin),
   576  			"complaint was already received")
   577  		return
   578  	}
   579  	c.received = true
   580  	// answerReceived flag check is a sanity check
   581  	if s.vAReceived && c.answerReceived && s.myIndex != s.dealerIndex {
   582  		s.disqualified = s.checkComplaint(origin, c)
   583  		if s.disqualified {
   584  			s.processor.Disqualify(int(s.dealerIndex),
   585  				fmt.Sprintf("complaint received: complaint answer to %d is invalid",
   586  					origin))
   587  		}
   588  		return
   589  	}
   590  }
   591  
   592  // answer = |complainer| private share |
   593  func (s *feldmanVSSQualState) receiveComplaintAnswer(origin index, data []byte) {
   594  	// check for invalid answers
   595  	if origin != s.dealerIndex {
   596  		return
   597  	}
   598  
   599  	// check the answer format
   600  	if len(data) != complaintAnswerSize {
   601  		s.disqualified = true
   602  		s.processor.Disqualify(int(s.dealerIndex),
   603  			fmt.Sprintf("the complaint answer has an invalid length, expects %d, got %d",
   604  				complaintAnswerSize, len(data)))
   605  		return
   606  	}
   607  
   608  	// first byte encodes the complainee
   609  	complainer := index(data[0])
   610  	if int(complainer) >= s.size {
   611  		s.disqualified = true
   612  		s.processor.Disqualify(int(origin),
   613  			fmt.Sprintf("complainer value is invalid, should be less that %d, got %d",
   614  				s.size, int(complainer)))
   615  		return
   616  	}
   617  
   618  	c, ok := s.complaints[complainer]
   619  	// if the complaint is new, add it
   620  	if !ok {
   621  		s.complaints[complainer] = &complaint{
   622  			received:       false,
   623  			answerReceived: true,
   624  		}
   625  
   626  		// read the complainer private share
   627  		C.bn_new_wrapper((*C.bn_st)(&s.complaints[complainer].answer))
   628  		if C.bn_read_Zr_bin((*C.bn_st)(&s.complaints[complainer].answer),
   629  			(*C.uchar)(&data[1]),
   630  			PrKeyLenBLSBLS12381,
   631  		) != valid {
   632  			s.disqualified = true
   633  			s.processor.Disqualify(int(s.dealerIndex),
   634  				fmt.Sprintf("invalid complaint answer value %x", data))
   635  			return
   636  		}
   637  		return
   638  	}
   639  	// complaint is not new in the map
   640  	// check if the answer has been already received
   641  	if c.answerReceived {
   642  		s.processor.FlagMisbehavior(int(origin),
   643  			"complaint answer was already received")
   644  		return
   645  	}
   646  	c.answerReceived = true
   647  
   648  	// flag check is a sanity check
   649  	if c.received {
   650  		// read the complainer private share
   651  		C.bn_new_wrapper((*C.bn_st)(&c.answer))
   652  		if C.bn_read_Zr_bin((*C.bn_st)(&c.answer),
   653  			(*C.uchar)(&data[1]),
   654  			PrKeyLenBLSBLS12381,
   655  		) != valid {
   656  			s.disqualified = true
   657  			s.processor.Disqualify(int(s.dealerIndex),
   658  				fmt.Sprintf("invalid complaint answer value %x", data))
   659  			return
   660  		}
   661  		if s.vAReceived {
   662  			s.disqualified = s.checkComplaint(complainer, c)
   663  			if s.disqualified {
   664  				s.processor.Disqualify(int(s.dealerIndex),
   665  					fmt.Sprintf("complaint answer received: complaint answer to %d is invalid",
   666  						complainer))
   667  			}
   668  		}
   669  
   670  		// fix the share of the current participant if the complaint is invalid
   671  		if !s.disqualified && complainer == s.myIndex {
   672  			s.x = c.answer
   673  		}
   674  	}
   675  }