github.com/annchain/OG@v0.0.9/consensus/dkg/dkg_partner.go (about)

     1  package dkg
     2  
     3  import (
     4  	"github.com/annchain/OG/arefactor/common/goroutine"
     5  	"github.com/annchain/OG/common/hexutil"
     6  	"github.com/annchain/OG/ffchan"
     7  	"github.com/annchain/kyber/v3"
     8  	"github.com/annchain/kyber/v3/pairing/bn256"
     9  	dkger "github.com/annchain/kyber/v3/share/dkg/pedersen"
    10  	vss "github.com/annchain/kyber/v3/share/vss/pedersen"
    11  	"github.com/sirupsen/logrus"
    12  	"sync"
    13  	"time"
    14  )
    15  
    16  // DefaultDkgPartner is the parter in a DKG group built to discuss a pub/privkey
    17  // It will receive DKG messages and update the status.
    18  // It is the handler for maintaining the DkgContext.
    19  // Campaign or term change is not part of DKGPartner. Do their job in their own module.
    20  type DefaultDkgPartner struct {
    21  	context                  *DkgContext
    22  	peerCommunicatorIncoming DkgPeerCommunicatorIncoming
    23  	peerCommunicatorOutgoing DkgPeerCommunicatorOutgoing
    24  	dealReceivingCache       DisorderedCache // map[deal_sender_index]Deal
    25  	gossipStartCh            chan bool
    26  
    27  	otherPeers            []DkgPeer
    28  	notified              bool
    29  	DealResponseCache     map[int]*dkger.Response //my response for such deal should not be generated twice
    30  	ResponseCache         map[string]bool         // duplicate response should not be processed twice.
    31  	total                 int
    32  	dkgGeneratedListeners []DkgGeneratedListener // joint pubkey is got
    33  
    34  	quit   chan bool
    35  	quitWg sync.WaitGroup
    36  }
    37  
    38  func (p *DefaultDkgPartner) GetDkgPeerCommunicatorIncoming() DkgPeerCommunicatorIncoming {
    39  	return p.peerCommunicatorIncoming
    40  }
    41  
    42  func (p *DefaultDkgPartner) Stop() {
    43  	close(p.quit)
    44  	p.quitWg.Wait()
    45  }
    46  
    47  // NewDefaultDkgPartner inits a dkg group. All public keys should be already generated
    48  // The public keys are shared before the Dkg group can be formed.
    49  // This may be done by publishing partPub to the blockchain
    50  // termId is still needed to identify different Dkg groups
    51  // allPeers needs to be sorted and globally order identical
    52  func NewDefaultDkgPartner(suite *bn256.Suite, termId uint32, numParts, threshold int, allPeers []PartPub, me PartSec,
    53  	dkgPeerCommunicatorIncoming DkgPeerCommunicatorIncoming,
    54  	dkgPeerCommunicatorOutgoing DkgPeerCommunicatorOutgoing) (*DefaultDkgPartner, error) {
    55  	// new dkg context
    56  	c := NewDkgContext(suite, termId)
    57  	c.NbParticipants = numParts
    58  	c.Threshold = threshold
    59  	c.PartPubs = allPeers
    60  	c.Me = me
    61  
    62  	// find Who am I
    63  	myIndex := -1
    64  	for i := 0; i < len(allPeers); i++ {
    65  		if allPeers[i].Point.Equal(me.Point) {
    66  			// That's me
    67  			myIndex = i
    68  			break
    69  		}
    70  	}
    71  	if myIndex == -1 {
    72  		panic("did not find myself")
    73  	}
    74  	c.MyIndex = uint32(myIndex)
    75  
    76  	// setup partner
    77  	d := &DefaultDkgPartner{
    78  		context:                  c,
    79  		peerCommunicatorIncoming: dkgPeerCommunicatorIncoming,
    80  		peerCommunicatorOutgoing: dkgPeerCommunicatorOutgoing,
    81  		dealReceivingCache:       make(DisorderedCache),
    82  		gossipStartCh:            make(chan bool),
    83  		otherPeers:               []DkgPeer{},
    84  		notified:                 false,
    85  		DealResponseCache:        make(map[int]*dkger.Response),
    86  		ResponseCache:            make(map[string]bool),
    87  		total:                    0,
    88  		dkgGeneratedListeners:    []DkgGeneratedListener{},
    89  		quit:                     make(chan bool),
    90  		quitWg:                   sync.WaitGroup{},
    91  	}
    92  
    93  	// init all other peers so that I can do broadcast
    94  	for i := 0; i < len(allPeers); i++ {
    95  		if i == int(c.MyIndex) {
    96  			continue
    97  		}
    98  		d.otherPeers = append(d.otherPeers, allPeers[i].Peer)
    99  	}
   100  
   101  	if err := c.GenerateDKGer(); err != nil {
   102  		// cannot build dkg group using these pubkeys
   103  		return nil, err
   104  	}
   105  	return d, nil
   106  
   107  }
   108  
   109  // GenPartnerPair generates a part private/public key for discussing with others.
   110  func GenPartnerPair(suite *bn256.Suite) (kyber.Scalar, kyber.Point) {
   111  	sc := suite.Scalar().Pick(suite.RandomStream())
   112  	return sc, suite.Point().Mul(sc, nil)
   113  }
   114  
   115  func (p *DefaultDkgPartner) Start() {
   116  	// start to gossipLoop and share the deals
   117  	p.quitWg.Add(1)
   118  	goroutine.New(p.gossipLoop)
   119  }
   120  
   121  func (p *DefaultDkgPartner) gossipLoop() {
   122  	select {
   123  	case <-p.gossipStartCh:
   124  		logrus.Debug("dkg gossip started")
   125  		break
   126  	case <-p.quit:
   127  		logrus.Debug("dkg gossip quit")
   128  		return
   129  	}
   130  	pipeOutChannel := p.peerCommunicatorIncoming.GetPipeOut()
   131  	// send the deals to all other partners
   132  	go p.announceDeals()
   133  	for {
   134  		timer := time.NewTimer(time.Second * 10)
   135  		select {
   136  		case <-p.quit:
   137  			logrus.Warn("dkg gossip quit")
   138  			p.quitWg.Done()
   139  			return
   140  		case <-timer.C:
   141  			logrus.WithField("IM", p.context.Me.Peer.Address.ShortString()).Warn("Blocked reading incoming dkg")
   142  			//p.checkWaitingForWhat()
   143  		case msgEvent := <-pipeOutChannel:
   144  			logrus.WithField("me", p.context.MyIndex).WithField("type", msgEvent.Message.GetType()).Trace("received a message")
   145  			p.handleMessage(msgEvent)
   146  		}
   147  	}
   148  
   149  }
   150  
   151  // announceDeals sends deals to all other partners to build up a dkg group
   152  func (p *DefaultDkgPartner) announceDeals() {
   153  	// get all deals that needs to be sent to other partners
   154  	deals, err := p.context.Dkger.Deals()
   155  	if err != nil {
   156  		logrus.WithError(err).Fatal("failed to generate dkg deals")
   157  	}
   158  	for i, deal := range deals {
   159  		p.sendDealToPartner(i, deal)
   160  	}
   161  
   162  	// in case of package loss, keep resending
   163  	//for {
   164  	//	time.Sleep(time.Second * 60)
   165  	//	logrus.Warn("RESEND")
   166  	//	for i, deal := range deals {
   167  	//		p.sendDealToPartner(i, deal)
   168  	//	}
   169  	//}
   170  }
   171  
   172  // sendDealToPartner unicast a deal message to some specific partner
   173  func (p *DefaultDkgPartner) sendDealToPartner(id int, deal *dkger.Deal) {
   174  	data, err := deal.MarshalMsg(nil)
   175  	if err != nil {
   176  		logrus.WithError(err).Fatal("cannot marshal dkg deal")
   177  	}
   178  
   179  	msg := &MessageDkgDeal{
   180  		DkgBasicInfo: DkgBasicInfo{
   181  			TermId: p.context.SessionId,
   182  		},
   183  		Data: data,
   184  	}
   185  	logrus.WithField("from", deal.Index).WithField("to", id).
   186  		Trace("unicasting deal message")
   187  	p.peerCommunicatorOutgoing.Unicast(msg, p.context.PartPubs[id].Peer)
   188  	// after this, you are expecting a response from the target peers
   189  }
   190  
   191  func (p *DefaultDkgPartner) sendResponseToAllRestPartners(response *dkger.Response) {
   192  	data, err := response.MarshalMsg(nil)
   193  	if err != nil {
   194  		// TODO: change it to warn maybe
   195  		logrus.WithError(err).Fatal("cannot marshal dkg response")
   196  		return
   197  	}
   198  
   199  	msg := &MessageDkgDealResponse{
   200  		DkgBasicInfo: DkgBasicInfo{
   201  			TermId: p.context.SessionId,
   202  		},
   203  		Data: data,
   204  	}
   205  	logrus.WithField("me", p.context.MyIndex).WithField("from", response.Response.Index).Trace("broadcasting response message")
   206  	p.peerCommunicatorOutgoing.Broadcast(msg, p.otherPeers)
   207  }
   208  
   209  func (p *DefaultDkgPartner) handleMessage(msgEvent *DkgMessageEvent) {
   210  	message := msgEvent.Message
   211  	switch message.GetType() {
   212  	case DkgMessageTypeDeal:
   213  		msg, ok := message.(*MessageDkgDeal)
   214  		if !ok {
   215  			logrus.Warn("it claims to be a MessageDkgDeal but the payload does not align")
   216  			return
   217  		}
   218  		p.handleDealMessage(msg)
   219  	case DkgMessageTypeDealResponse:
   220  		msg, ok := message.(*MessageDkgDealResponse)
   221  		if !ok {
   222  			logrus.Warn("it claims to be a MessageDkgDealResponse but the payload does not align")
   223  			return
   224  		}
   225  		p.handleDealResponseMessage(msg)
   226  	default:
   227  		logrus.WithField("type", message.GetType()).Warn("unknown dkg message type")
   228  	}
   229  }
   230  
   231  func (p *DefaultDkgPartner) handleDealMessage(msg *MessageDkgDeal) {
   232  	deal, err := msg.GetDeal()
   233  	if err != nil {
   234  		logrus.Warn("failed to unmarshal dkg deal message")
   235  		return
   236  	}
   237  	// verify if deal's sender has a unified index and pubkey to avoid fake messages.
   238  	err = p.verifyDealSender(msg, deal)
   239  	if err != nil {
   240  		logrus.WithError(err).Warn("wrong sender for dkg deal")
   241  		return
   242  	}
   243  
   244  	issuerIndex := deal.Index
   245  	v, hasPreviousDiscussion := p.dealReceivingCache[issuerIndex]
   246  	if !hasPreviousDiscussion {
   247  		v = &DkgDiscussion{
   248  			Deal:      nil,
   249  			Responses: []*dkger.Response{},
   250  		}
   251  		logrus.WithField("me", p.context.MyIndex).WithField("from", deal.Index).Trace("no previous discussion found in cache. new deal.")
   252  	} else {
   253  		logrus.WithField("me", p.context.MyIndex).WithField("from", deal.Index).Trace("previous discussion found in cache.")
   254  	}
   255  
   256  	//resp, inDealResponseCache := p.DealResponseCache[hexutil.Encode(deal.Signature)]
   257  	resp, inDealResponseCache := p.DealResponseCache[int(deal.Index)]
   258  	// if the resp is already generated, do not generate for the second time since it will error out.
   259  	if !inDealResponseCache {
   260  		// generate response and send out
   261  		//p.dumpDeal(deal)
   262  		//p.writeMessage(fmt.Sprintf("Process Deal %d", deal.Index))
   263  		resp, err := p.context.Dkger.ProcessDeal(deal)
   264  		if err != nil {
   265  			logrus.WithError(err).Warn("failed to process deal")
   266  			return
   267  		}
   268  		if resp.Response.Status != vss.StatusApproval {
   269  			logrus.Warn("received a deal for rejection")
   270  		}
   271  		// cache the deal
   272  		discussion := v.(*DkgDiscussion)
   273  		discussion.Deal = deal
   274  
   275  		// update state
   276  		p.dealReceivingCache[issuerIndex] = discussion
   277  
   278  		// send response to all other partners except itself
   279  		//p.writeMessage(fmt.Sprintf("Send Resp %d %d", resp.Index, resp.Response.Index))
   280  		p.sendResponseToAllRestPartners(resp)
   281  		// avoid response regeneration
   282  		//p.DealResponseCache[hexutil.Encode(deal.Signature)] = resp
   283  		p.DealResponseCache[int(deal.Index)] = resp
   284  
   285  		// TODOļ¼š BUGGY
   286  		if hasPreviousDiscussion && discussion.GetCurrentStage() >= StageDealReceived {
   287  			// now deal is just coming. Process the previous deal Responses
   288  			for _, response := range discussion.Responses {
   289  				//p.writeMessage(fmt.Sprintf("Process cached Resp %d %d", response.Index, response.Response.Index))
   290  				p.handleResponse(response)
   291  			}
   292  			// clear the discussion response list since we won't process it twice
   293  			discussion.Responses = []*dkger.Response{}
   294  		} else {
   295  			//p.writeMessage(fmt.Sprintf("not a cached way: %d", discussion.Deal.Index))
   296  		}
   297  	} else {
   298  		// RE-send response to all other partners except itself
   299  		//p.writeMessage(fmt.Sprintf("Send Resp %d %d", resp.Index, resp.Response.Index))
   300  		p.sendResponseToAllRestPartners(resp)
   301  	}
   302  }
   303  
   304  func (p *DefaultDkgPartner) handleDealResponseMessage(msg *MessageDkgDealResponse) {
   305  	resp, err := msg.GetResponse()
   306  	if err != nil {
   307  		logrus.Warn("failed to unmarshal dkg response message")
   308  		return
   309  	}
   310  	//p.dumpDealResponseMessage(resp, "received")
   311  	// verify if response's sender has a unified index and pubkey to avoid fake messages.
   312  	err = p.verifyResponseSender(msg, resp)
   313  	if err != nil {
   314  		logrus.WithError(err).Warn("wrong sender for dkg response")
   315  		return
   316  	}
   317  	// avoid duplication
   318  	signatureKey := hexutil.Encode(resp.Response.Signature)
   319  	_, responseDuplicated := p.ResponseCache[signatureKey]
   320  	if responseDuplicated {
   321  		logrus.WithField("me", p.context.MyIndex).
   322  			WithField("from", resp.Response.Index).
   323  			WithField("deal", resp.Index).Trace("duplicate response, drop")
   324  		return
   325  	} else {
   326  		p.ResponseCache[signatureKey] = true
   327  	}
   328  
   329  	// check if the correspondant deal is in the cache
   330  	// if not, hang on
   331  	dealerIndex := resp.Index
   332  	v, ok := p.dealReceivingCache[dealerIndex]
   333  	if !ok {
   334  		// deal from this sender has not been received. put the response to the cache
   335  		v = &DkgDiscussion{
   336  			Deal:      nil,
   337  			Responses: []*dkger.Response{},
   338  		}
   339  	}
   340  	// currently whatever deal is there, append the response to the cache.
   341  	// in the future this may be removed once deal is received.
   342  	discussion := v.(*DkgDiscussion)
   343  	discussion.Responses = append(discussion.Responses, resp)
   344  	// update state
   345  	p.dealReceivingCache[dealerIndex] = discussion
   346  	//verifierIndex := resp.Response.Index
   347  
   348  	// if deal is already there, process this response
   349  	if discussion.Deal != nil || dealerIndex == p.context.MyIndex {
   350  		logrus.WithField("me", p.context.MyIndex).
   351  			WithField("from", resp.Response.Index).
   352  			WithField("deal", resp.Index).Trace("new resp is being processed")
   353  		//p.writeMessage(fmt.Sprintf("Process Resp %d %d", resp.Index, resp.Response.Index))
   354  		p.handleResponse(resp)
   355  	} else {
   356  		logrus.WithField("me", p.context.MyIndex).
   357  			WithField("from", resp.Response.Index).
   358  			WithField("deal", resp.Index).Trace("new resp is being cached")
   359  		//p.writeMessage(fmt.Sprintf("Cached Resp %d %d", resp.Index, resp.Response.Index))
   360  	}
   361  }
   362  
   363  func (p *DefaultDkgPartner) handleResponse(resp *dkger.Response) {
   364  	//p.dumpDealResponseMessage(resp, "realin")
   365  	justification, err := p.context.Dkger.ProcessResponse(resp)
   366  	if err != nil {
   367  		logrus.WithError(err).WithField("me", p.context.MyIndex).
   368  			WithField("from", resp.Response.Index).
   369  			WithField("deal", resp.Index).Warn("error on processing response")
   370  		return
   371  	}
   372  	if justification != nil {
   373  		logrus.Warn("justification not nil")
   374  		// TODO: broadcast the justificaiton to the others to inform that this is a bad node
   375  	}
   376  	logrus.WithField("me", p.context.MyIndex).
   377  		WithField("from", resp.Response.Index).
   378  		WithField("deal", resp.Index).Trace("response is ok")
   379  	if !p.notified && p.context.Dkger.ThresholdCertified() {
   380  		_, err := p.context.RecoverPub()
   381  		if err != nil {
   382  			logrus.WithField("me", p.context.MyIndex).Warn("DKG has been generated but pubkey recovery failed")
   383  		} else {
   384  			logrus.WithField("me", p.context.MyIndex).WithField("pk", p.context.JointPubKey.String()).Info("DKG has been generated")
   385  			p.notifyListeners()
   386  			//p.checkWaitingForWhat()
   387  		}
   388  	}
   389  }
   390  
   391  func (p *DefaultDkgPartner) verifyDealSender(deal *MessageDkgDeal, deal2 *dkger.Deal) error {
   392  	return nil
   393  }
   394  
   395  func (p *DefaultDkgPartner) verifyResponseSender(response *MessageDkgDealResponse, deal *dkger.Response) error {
   396  	return nil
   397  }
   398  
   399  // notifyListeners notifies listeners who has been registered for dkg generated events
   400  func (p *DefaultDkgPartner) notifyListeners() {
   401  	for _, listener := range p.dkgGeneratedListeners {
   402  		<-ffchan.NewTimeoutSenderShort(listener.GetDkgGeneratedEventChannel(), true, "listener").C
   403  		//listener.GetDkgGeneratedEventChannel() <- true
   404  	}
   405  	p.notified = true
   406  }
   407  
   408  func (p *DefaultDkgPartner) RegisterDkgGeneratedListener(l DkgGeneratedListener) {
   409  	p.dkgGeneratedListeners = append(p.dkgGeneratedListeners, l)
   410  }
   411  
   412  //func (p *DefaultDkgPartner) GetPeerCommunicatorOutgoing() DkgPeerCommunicatorOutgoing {
   413  //	return p.peerCommunicatorOutgoing
   414  //}
   415  //
   416  //func (p *DefaultDkgPartner) GetPeerCommunicatorIncoming() DkgPeerCommunicatorIncoming {
   417  //	return p.peerCommunicatorIncoming
   418  //}
   419  
   420  //
   421  //func (p *DefaultDkgPartner) checkWaitingForWhat() {
   422  //
   423  //	//if p.context.MyIndex != 0 {notified
   424  //	//	return
   425  //	//}
   426  //	total := TestNodes
   427  //	if !p.notified {
   428  //		logrus.WithField("IM", p.context.MyIndex).
   429  //			WithField("qual", p.context.Dkger.QUAL()).
   430  //			Warn("not notified")
   431  //	} else {
   432  //		return
   433  //	}
   434  //	logrus.WithField("me", p.context.MyIndex).
   435  //		WithField("qual", len(p.context.Dkger.QUAL())).
   436  //		WithField("notified", p.notified).
   437  //		Info("check waiting for what")
   438  //
   439  //	var dealers []int
   440  //
   441  //	for dealer, v := range p.dealReceivingCache {
   442  //		dealers = append(dealers, int(dealer))
   443  //		discussion := v.(*DkgDiscussion)
   444  //		if dealer != p.context.MyIndex && len(discussion.Responses) != total-2 {
   445  //			logrus.WithFields(logrus.Fields{
   446  //				"IM":  p.context.MyIndex,
   447  //				"len": len(discussion.Responses),
   448  //			}).Warn("missing")
   449  //			for _, response := range discussion.Responses {
   450  //				logrus.WithField("IM", p.context.MyIndex).WithField("deal index", response.Index).
   451  //					WithField("resp index", response.Response.Index).Warn("resp index")
   452  //			}
   453  //		} else if dealer == p.context.MyIndex && len(discussion.Responses) != total-1 {
   454  //			logrus.WithFields(logrus.Fields{
   455  //				"IM":  p.context.MyIndex,
   456  //				"len": len(discussion.Responses),
   457  //			}).Warn("missing")
   458  //			for _, response := range discussion.Responses {
   459  //				logrus.WithField("IM", p.context.MyIndex).WithField("deal index", response.Index).
   460  //					WithField("resp index", response.Response.Index).Warn("resp index")
   461  //			}
   462  //		}
   463  //	}
   464  //	sort.Ints(dealers)
   465  //	logrus.WithField("dealers", dealers).WithField("ok", len(dealers) == total).WithField("IM", p.context.MyIndex).Info("all deals")
   466  //}
   467  //
   468  //func (p *DefaultDkgPartner) dumpDeal(deal *dkger.Deal) {
   469  //	return
   470  //	debugPath := "D:/tmp/debug"
   471  //	file, err := os.OpenFile(
   472  //		path.Join(debugPath, fmt.Sprintf("deal_%02d.txt", p.context.MyIndex)),
   473  //		os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   474  //	if err != nil {
   475  //		panic(err)
   476  //	}
   477  //	defer file.Close()
   478  //	_, err = fmt.Fprintf(file, "%d\r\n", deal.Index)
   479  //	if err != nil {
   480  //		panic(err)
   481  //	}
   482  //}
   483  //
   484  //func (p *DefaultDkgPartner) dumpDealResponseMessage(response *dkger.Response, msg string) {
   485  //	p.total += 1
   486  //	if p.context.MyIndex == 0  && p.total % 20 == 0{
   487  //		fmt.Println(p.total)
   488  //	}
   489  //	return
   490  //	debugPath := "D:/tmp/debug"
   491  //	file, err := os.OpenFile(
   492  //		path.Join(debugPath, fmt.Sprintf("resp_%02d_%s.txt", p.context.MyIndex, msg)),
   493  //		os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   494  //	if err != nil {
   495  //		panic(err)
   496  //	}
   497  //	defer file.Close()
   498  //	_, err = fmt.Fprintf(file, "%d %d\r\n", response.Index, response.Response.Index)
   499  //	if err != nil {
   500  //		panic(err)
   501  //	}
   502  //}
   503  //
   504  //func (p *DefaultDkgPartner) writeMessage(msg string) {
   505  //	return
   506  //	debugPath := "D:/tmp/debug"
   507  //	file, err := os.OpenFile(
   508  //		path.Join(debugPath, fmt.Sprintf("log_%d.txt", p.context.MyIndex)),
   509  //		os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   510  //	if err != nil {
   511  //		panic(err)
   512  //	}
   513  //	defer file.Close()
   514  //	_, err = fmt.Fprintf(file, "%s\r\n", msg)
   515  //	if err != nil {
   516  //		panic(err)
   517  //	}
   518  //}