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 }