github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/dkg/controller.go (about)

     1  package dkg
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/onflow/crypto"
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/module"
    12  )
    13  
    14  // Controller implements the DKGController interface. It controls the execution
    15  // of a Joint Feldman DKG instance. A new Controller must be instantiated for
    16  // every epoch.
    17  type Controller struct {
    18  	// The embedded state Manager is used to manage the controller's underlying
    19  	// state.
    20  	Manager
    21  
    22  	log zerolog.Logger
    23  
    24  	// DKGState is the object that actually executes the protocol steps.
    25  	dkg crypto.DKGState
    26  
    27  	// dkgLock protects access to dkg
    28  	dkgLock sync.Mutex
    29  
    30  	// seed is required by DKGState
    31  	seed []byte
    32  
    33  	// broker enables the controller to communicate with other nodes
    34  	broker module.DKGBroker
    35  
    36  	// Channels used internally to trigger state transitions
    37  	h1Ch       chan struct{}
    38  	h2Ch       chan struct{}
    39  	endCh      chan struct{}
    40  	shutdownCh chan struct{}
    41  
    42  	// private fields that hold the DKG artifacts when the protocol runs to
    43  	// completion
    44  	privateShare   crypto.PrivateKey
    45  	publicKeys     []crypto.PublicKey
    46  	groupPublicKey crypto.PublicKey
    47  
    48  	// artifactsLock protects access to artifacts
    49  	artifactsLock sync.Mutex
    50  
    51  	once *sync.Once
    52  }
    53  
    54  // NewController instantiates a new Joint Feldman DKG controller.
    55  func NewController(
    56  	log zerolog.Logger,
    57  	dkgInstanceID string,
    58  	dkg crypto.DKGState,
    59  	seed []byte,
    60  	broker module.DKGBroker,
    61  ) *Controller {
    62  
    63  	logger := log.With().
    64  		Str("component", "dkg_controller").
    65  		Str("dkg_instance_id", dkgInstanceID).
    66  		Logger()
    67  
    68  	return &Controller{
    69  		log:        logger,
    70  		dkg:        dkg,
    71  		seed:       seed,
    72  		broker:     broker,
    73  		h1Ch:       make(chan struct{}),
    74  		h2Ch:       make(chan struct{}),
    75  		endCh:      make(chan struct{}),
    76  		shutdownCh: make(chan struct{}),
    77  		once:       new(sync.Once),
    78  	}
    79  }
    80  
    81  /*******************************************************************************
    82  Implement DKGController
    83  *******************************************************************************/
    84  
    85  // Run starts the DKG controller and executes the DKG state-machine. It blocks
    86  // until the controller is shutdown or until an error is encountered in one of
    87  // the protocol phases.
    88  func (c *Controller) Run() error {
    89  
    90  	// Start DKG and transition to phase 1
    91  	err := c.start()
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	// Start a background routine to listen for incoming private and broadcast
    97  	// messages from other nodes
    98  	go c.doBackgroundWork()
    99  
   100  	// Execute DKG State Machine
   101  	for {
   102  		state := c.GetState()
   103  		c.log.Debug().Msgf("DKG: %s", c.state)
   104  
   105  		switch state {
   106  		case Phase1:
   107  			err := c.phase1()
   108  			if err != nil {
   109  				return err
   110  			}
   111  		case Phase2:
   112  			err := c.phase2()
   113  			if err != nil {
   114  				return err
   115  			}
   116  		case Phase3:
   117  			err := c.phase3()
   118  			if err != nil {
   119  				return err
   120  			}
   121  		case End:
   122  			c.Shutdown()
   123  		case Shutdown:
   124  			return nil
   125  		}
   126  	}
   127  }
   128  
   129  // EndPhase1 notifies the controller to end phase 1, and start phase 2
   130  func (c *Controller) EndPhase1() error {
   131  	state := c.GetState()
   132  	if state != Phase1 {
   133  		return NewInvalidStateTransitionError(state, Phase2)
   134  	}
   135  
   136  	c.SetState(Phase2)
   137  	close(c.h1Ch)
   138  
   139  	return nil
   140  }
   141  
   142  // EndPhase2 notifies the controller to end phase 2, and start phase 3
   143  func (c *Controller) EndPhase2() error {
   144  	state := c.GetState()
   145  	if state != Phase2 {
   146  		return NewInvalidStateTransitionError(state, Phase3)
   147  	}
   148  
   149  	c.SetState(Phase3)
   150  	close(c.h2Ch)
   151  
   152  	return nil
   153  }
   154  
   155  // End terminates the DKG state machine and records the artifacts.
   156  func (c *Controller) End() error {
   157  	state := c.GetState()
   158  	if state != Phase3 {
   159  		return NewInvalidStateTransitionError(state, End)
   160  	}
   161  
   162  	c.log.Debug().Msg("DKG engine end")
   163  
   164  	// end and retrieve products of the DKG protocol
   165  	c.dkgLock.Lock()
   166  
   167  	privateShare, groupPublicKey, publicKeys, err := c.dkg.End()
   168  	c.dkgLock.Unlock()
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	c.artifactsLock.Lock()
   174  	c.privateShare = privateShare
   175  	c.groupPublicKey = groupPublicKey
   176  	c.publicKeys = publicKeys
   177  	c.artifactsLock.Unlock()
   178  
   179  	c.SetState(End)
   180  	close(c.endCh)
   181  
   182  	return nil
   183  }
   184  
   185  // Shutdown stops the controller regardless of the current state.
   186  func (c *Controller) Shutdown() {
   187  	c.broker.Shutdown()
   188  	c.SetState(Shutdown)
   189  	close(c.shutdownCh)
   190  }
   191  
   192  // Poll instructs the broker to read new broadcast messages, which will be
   193  // relayed through the message channel. The function does not return until the
   194  // received messages are processed.
   195  func (c *Controller) Poll(blockReference flow.Identifier) error {
   196  	return c.broker.Poll(blockReference)
   197  }
   198  
   199  // GetArtifacts returns our node's private key share, the group public key,
   200  // and the list of all nodes' public keys (including ours), as computed by
   201  // the DKG.
   202  func (c *Controller) GetArtifacts() (crypto.PrivateKey, crypto.PublicKey, []crypto.PublicKey) {
   203  	c.artifactsLock.Lock()
   204  	defer c.artifactsLock.Unlock()
   205  	return c.privateShare, c.groupPublicKey, c.publicKeys
   206  }
   207  
   208  // GetIndex returns the index of this node in the DKG committee list.
   209  func (c *Controller) GetIndex() int {
   210  	return c.broker.GetIndex()
   211  }
   212  
   213  // SubmitResult instructs the broker to submit DKG results. It is up to the
   214  // caller to ensure that this method is called after a succesfull run of the
   215  // protocol.
   216  func (c *Controller) SubmitResult() error {
   217  	_, pubKey, groupKeys := c.GetArtifacts()
   218  	return c.broker.SubmitResult(pubKey, groupKeys)
   219  }
   220  
   221  /*******************************************************************************
   222  WORKERS
   223  *******************************************************************************/
   224  
   225  func (c *Controller) doBackgroundWork() {
   226  	privateMsgCh := c.broker.GetPrivateMsgCh()
   227  	broadcastMsgCh := c.broker.GetBroadcastMsgCh()
   228  	for {
   229  		select {
   230  		case msg := <-privateMsgCh:
   231  			c.dkgLock.Lock()
   232  			err := c.dkg.HandlePrivateMsg(int(msg.CommitteeMemberIndex), msg.Data)
   233  			c.dkgLock.Unlock()
   234  			if err != nil {
   235  				c.log.Err(err).Msg("error processing DKG private message")
   236  			}
   237  
   238  		case msg := <-broadcastMsgCh:
   239  
   240  			c.dkgLock.Lock()
   241  			err := c.dkg.HandleBroadcastMsg(int(msg.CommitteeMemberIndex), msg.Data)
   242  			c.dkgLock.Unlock()
   243  			if err != nil {
   244  				c.log.Err(err).Msg("error processing DKG broadcast message")
   245  			}
   246  
   247  		case <-c.shutdownCh:
   248  			return
   249  		}
   250  	}
   251  }
   252  
   253  func (c *Controller) start() error {
   254  	state := c.GetState()
   255  	if state != Init {
   256  		return fmt.Errorf("cannot execute start routine in state %s", state)
   257  	}
   258  
   259  	c.dkgLock.Lock()
   260  	err := c.dkg.Start(c.seed)
   261  	c.dkgLock.Unlock()
   262  	if err != nil {
   263  		return fmt.Errorf("Error starting DKG: %w", err)
   264  	}
   265  
   266  	c.log.Debug().Msg("DKG engine started")
   267  	c.SetState(Phase1)
   268  	return nil
   269  }
   270  
   271  func (c *Controller) phase1() error {
   272  	state := c.GetState()
   273  	if state != Phase1 {
   274  		return fmt.Errorf("Cannot execute phase1 routine in state %s", state)
   275  	}
   276  
   277  	c.log.Debug().Msg("Waiting for end of phase 1")
   278  	for {
   279  		select {
   280  		case <-c.h1Ch:
   281  			return nil
   282  		case <-c.shutdownCh:
   283  			return nil
   284  		}
   285  	}
   286  }
   287  
   288  func (c *Controller) phase2() error {
   289  	state := c.GetState()
   290  	if state != Phase2 {
   291  		return fmt.Errorf("Cannot execute phase2 routine in state %s", state)
   292  	}
   293  
   294  	c.dkgLock.Lock()
   295  	err := c.dkg.NextTimeout()
   296  	c.dkgLock.Unlock()
   297  	if err != nil {
   298  		return fmt.Errorf("Error calling NextTimeout: %w", err)
   299  	}
   300  
   301  	c.log.Debug().Msg("Waiting for end of phase 2")
   302  	for {
   303  		select {
   304  		case <-c.h2Ch:
   305  			return nil
   306  		case <-c.shutdownCh:
   307  			return nil
   308  		}
   309  	}
   310  }
   311  
   312  func (c *Controller) phase3() error {
   313  	state := c.GetState()
   314  	if state != Phase3 {
   315  		return fmt.Errorf("Cannot execute phase3 routine in state %s", state)
   316  	}
   317  
   318  	c.dkgLock.Lock()
   319  	err := c.dkg.NextTimeout()
   320  	c.dkgLock.Unlock()
   321  	if err != nil {
   322  		return fmt.Errorf("Error calling NextTimeout: %w", err)
   323  	}
   324  
   325  	c.log.Debug().Msg("Waiting for end of phase 3")
   326  	for {
   327  		select {
   328  		case <-c.endCh:
   329  			return nil
   330  		case <-c.shutdownCh:
   331  			return nil
   332  		}
   333  	}
   334  }