dkg

package
v0.39.2-en-pebble Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2025 License: AGPL-3.0 Imports: 29 Imported by: 3

Documentation

Overview

Package dkg implements a controller that manages the lifecycle of a Joint Feldman DKG node, as well as a broker that enables the controller to communicate with other nodes

Controller

A new controller must be instantiated for every epoch.

The state-machine can be represented as follows:

+-------+  /Run() +---------+  /EndPhase1() +---------+  /EndPhase2() +---------+  /End()   +-----+     +----------+
| Init  | ----->  | Phase 1 | ------------> | Phase 2 | ------------> | Phase 3 | --------> | End | --> | Shutdown |
+-------+         +---------+               +---------+               +---------+           +-----+     +----------+
|                   |                         |                         |                                 ^
v___________________v_________________________v_________________________v_________________________________|
                                         /Shutdown()

The controller is always in one of 6 states:

- Init: Default state before the instance is started - Phase 1: 1st phase of the JF DKG protocol while it's running - Phase 2: 2nd phase --- - Phase 3: 3rd phase --- - End: When the DKG protocol is finished - Shutdown: When the controller and all its routines are stopped

The controller exposes the following functions to trigger transitions:

Run(): Triggers transition from Init to Phase1. Starts the DKG protocol instance and background communication routines.

EndPhase1(): Triggers transition from Phase 1 to Phase 2.

EndPhase2(): Triggers transition from Phase 2 to Phase 3.

End(): Ends the DKG protocol and records the artifacts in controller. Triggers transition from Phase 3 to End.

Shutdown(): Can be called from any state to stop the DKG instance.

The End and Shutdown states differ in that the End state can only be arrived at from Phase 3 and after successfully computing the DKG artifacts. Whereas the Shutdown state can be reached from any other state.

Broker

The controller requires a broker to communicate with other nodes over the network and to read broadcast messages from the DKG smart-contract. A new broker must be instantiated for every epoch.

The Broker is responsible for:

  • converting to and from the message format used by the underlying crypto DKG package.
  • appending dkg instance id to messages to prevent replay attacks
  • checking the integrity of incoming messages
  • signing and verifying broadcast messages (broadcast messages are signed with the staking key of the sender)
  • forwarding incoming messages (private and broadcast) to the controller via a channel
  • forwarding outgoing messages (private and broadcast) to other nodes.

+------------+ +-------------+ | | | | <--------(tunnel)-----------> network engine <--> Other nodes | Controller |--| Broker | | | | | <--(smart-contract client)--> DKG smart-contract +------------+ +-------------+

To relay private messages, the broker uses a BrokerTunnel to communicate with a network engine.

To send and receive broadcast messages, the broker communicates with the DKG smart-contract via a smart-contract client. The broker's Poll method must be called regularly to read broadcast messages from the smart-contract.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CanonicalInstanceID

func CanonicalInstanceID(chainID flow.ChainID, epochCounter uint64) string

CanonicalInstanceID returns the canonical DKG instance ID for the given epoch on the given chain.

func IsInvalidStateTransitionError

func IsInvalidStateTransitionError(err error) bool

func NewDKGMessageHasher

func NewDKGMessageHasher() hash.Hasher

NewDKGMessageHasher returns a hasher for signing and verifying DKG broadcast messages.

Types

type BeaconKeyRecovery added in v0.39.0

type BeaconKeyRecovery struct {
	events.Noop
	// contains filtered or unexported fields
}

BeaconKeyRecovery is a specific module that attempts automatic recovery of the random beacon private key when exiting Epoch Fallback Mode [EFM]. In the happy path of the protocol, each node that takes part in the DKG obtains a random beacon private key, which is stored in storage.DKGState. If the network enters EFM, the network can be recovered via the flow.EpochRecover service event, which exits EFM by specifying the subsequent epoch ("recovery epoch"). This recovery epoch must have a Random Beacon committee with valid keys, but no successful DKG occurred. To solve this, by convention, we require the recovery epoch to re-use the Random Beacon public keys from the most recent successful DKG. Upon observing that EFM was exited, this component:

  • looks up its Random Beacon private key from the last DKG
  • looks up the Random Beacon public keys specified for the recovery epoch
  • validates that its private key is compatible (matches the public key specified)
  • if valid, persists that private key as the safe beacon key for the recovery epoch

func NewBeaconKeyRecovery added in v0.39.0

func NewBeaconKeyRecovery(
	log zerolog.Logger,
	local module.Local,
	state protocol.State,
	localDKGState storage.EpochRecoveryMyBeaconKey,
) (*BeaconKeyRecovery, error)

NewBeaconKeyRecovery creates a new BeaconKeyRecovery instance and tries to recover the random beacon private key. This method ensures that we try to recover the random beacon private key even if we have missed the `EpochFallbackModeExited` protocol event (this could happen if the node has crashed between emitting and delivering the event). No errors are expected during normal operations.

func (*BeaconKeyRecovery) EpochFallbackModeExited added in v0.39.0

func (b *BeaconKeyRecovery) EpochFallbackModeExited(epochCounter uint64, refBlock *flow.Header)

EpochFallbackModeExited implements handler from protocol.Consumer to perform recovery of the beacon private key when this node has exited the epoch fallback mode.

type Broker

type Broker struct {
	// contains filtered or unexported fields
}

Broker is an implementation of the DKGBroker interface which is intended to be used in conjunction with the DKG MessagingEngine for private messages, and with the DKG smart-contract for broadcast messages.

func NewBroker

func NewBroker(
	log zerolog.Logger,
	dkgInstanceID string,
	committee flow.IdentitySkeletonList,
	me module.Local,
	myIndex int,
	dkgContractClients []module.DKGContractClient,
	tunnel *BrokerTunnel,
	opts ...BrokerOpt,
) (*Broker, error)

NewBroker instantiates a new epoch-specific broker capable of communicating with other nodes via a network engine and dkg smart-contract. No errors are expected during normal operations.

func (*Broker) Broadcast

func (b *Broker) Broadcast(data []byte)

Broadcast signs and broadcasts a message to all participants.

func (*Broker) Disqualify

func (b *Broker) Disqualify(node int, log string)

Disqualify flags that a node is misbehaving and got disqualified

func (*Broker) FlagMisbehavior

func (b *Broker) FlagMisbehavior(node int, log string)

FlagMisbehavior warns that a node is misbehaving.

func (*Broker) GetBroadcastMsgCh

func (b *Broker) GetBroadcastMsgCh() <-chan messages.BroadcastDKGMessage

GetBroadcastMsgCh returns the channel through which consumers can receive incoming broadcast DKG messages.

func (*Broker) GetIndex

func (b *Broker) GetIndex() int

GetIndex returns the index of this node in the committee list.

func (*Broker) GetPrivateMsgCh

func (b *Broker) GetPrivateMsgCh() <-chan messages.PrivDKGMessageIn

GetPrivateMsgCh returns the channel through which consumers can receive incoming private DKG messages.

func (*Broker) Poll

func (b *Broker) Poll(referenceBlock flow.Identifier) error

Poll calls the DKG smart contract to get missing DKG messages for the current epoch, and forwards them to the msgCh. It should be called with the ID of a block whose seal is finalized. The function doesn't return until the received messages are processed by the consumer because b.msgCh is not buffered.

func (*Broker) PrivateSend

func (b *Broker) PrivateSend(dest int, data []byte)

PrivateSend sends a DKGMessage to a destination over a private channel. It appends the current DKG instance ID to the message.

func (*Broker) Shutdown

func (b *Broker) Shutdown()

Shutdown stop the goroutine that listens to incoming private messages.

func (*Broker) SubmitResult

func (b *Broker) SubmitResult(groupKey crypto.PublicKey, pubKeys []crypto.PublicKey) error

SubmitResult publishes the result of the DKG protocol to the smart contract. This function should be passed the beacon keys (group key, participant keys) resulting from the DKG process. If the DKG process failed, no beacon keys will exist. In that case, we pass in nil here for both arguments.

If non-nil arguments are provided, we submit a non-empty ResultSubmission to the DKG smart contract, indicating that we completed the DKG successfully and essentially "voting for" our result. If nil arguments are provided, we submit an empty ResultSubmission to the DKG smart contract, indicating that we completed the DKG unsuccessfully.

type BrokerConfig added in v0.25.0

type BrokerConfig struct {
	// PublishMaxRetries is the maximum number of times the broker will attempt
	// to broadcast a message or publish a result.
	PublishMaxRetries uint64
	// ReadMaxRetries is the max number of times the broker will attempt to
	// read messages before giving up.
	ReadMaxRetries uint64
	// RetryMaxConsecutiveFailures is the number of consecutive failures allowed
	// before we switch to a different Access client for subsequent attempts.
	RetryMaxConsecutiveFailures int
	// RetryInitialWait is the initial duration to wait between retries for all
	// retryable requests - increases exponentially for subsequent retries.
	RetryInitialWait time.Duration
	// RetryJitterPct is the percentage jitter to introduce to each retry interval
	// for all retryable requests.
	RetryJitterPct uint64
}

BrokerConfig is configuration for the DKG Broker component.

func DefaultBrokerConfig added in v0.25.0

func DefaultBrokerConfig() BrokerConfig

DefaultBrokerConfig returns the default config for the DKG Broker component.

type BrokerOpt added in v0.25.0

type BrokerOpt func(*BrokerConfig)

BrokerOpt is a functional option which modifies the DKG Broker config.

type BrokerTunnel

type BrokerTunnel struct {
	MsgChIn  chan messages.PrivDKGMessageIn  // from network engine to broker
	MsgChOut chan messages.PrivDKGMessageOut // from broker to network engine
}

BrokerTunnel allows the DKG MessagingEngine to relay messages to and from a loosely-coupled Broker and Controller. The same BrokerTunnel is intended to be reused across epochs (multiple DKG instances). The BrokerTunnel does not internally queue messages, so sends through the tunnel are blocking.

func NewBrokerTunnel

func NewBrokerTunnel() *BrokerTunnel

NewBrokerTunnel instantiates a new BrokerTunnel.

func (*BrokerTunnel) SendIn

func (t *BrokerTunnel) SendIn(msg messages.PrivDKGMessageIn)

SendIn pushes incoming messages in the MsgChIn channel to be received by the Broker. This is a blocking call (messages are not queued within the tunnel)

func (*BrokerTunnel) SendOut

func (t *BrokerTunnel) SendOut(msg messages.PrivDKGMessageOut)

SendOut pushes outbound messages in the MsgChOut channel to be received and forwarded by the network engine. This is a blocking call (messages are not queued within the tunnel)

type Client

type Client struct {
	epochs.BaseClient
	// contains filtered or unexported fields
}

Client is a client to the Flow DKG contract. Allows functionality to Broadcast, read a Broadcast and submit the final result of the DKG protocol

func NewClient

func NewClient(
	log zerolog.Logger,
	flowClient module.SDKClientWrapper,
	flowClientANID flow.Identifier,
	signer sdkcrypto.Signer,
	dkgContractAddress,
	accountAddress string,
	accountKeyIndex uint32,
) *Client

NewClient initializes a new client to the Flow DKG contract

func (*Client) Broadcast

func (c *Client) Broadcast(msg model.BroadcastDKGMessage) error

Broadcast broadcasts a message to all other nodes participating in the DKG. The message is broadcast by submitting a transaction to the DKG smart contract. An error is returned if the transaction has failed.

func (*Client) ReadBroadcast

func (c *Client) ReadBroadcast(fromIndex uint, referenceBlock flow.Identifier) ([]model.BroadcastDKGMessage, error)

ReadBroadcast reads the broadcast messages from the smart contract. Messages are returned in the order in which they were received and stored in the smart contract

func (*Client) SubmitEmptyResult added in v0.39.0

func (c *Client) SubmitEmptyResult() error

SubmitEmptyResult submits an empty result of the DKG protocol. The empty result is obtained by a node when it realizes locally that its DKG participation was unsuccessful (either because the DKG failed as a whole, or because the node received too many byzantine inputs). However, a node obtaining an empty result can happen in both cases of the DKG succeeding or failing. For further details, please see: https://flowfoundation.notion.site/Random-Beacon-2d61f3b3ad6e40ee9f29a1a38a93c99c Honest nodes would call `SubmitEmptyResult` strictly after the final phase has ended if DKG has ended. Though, `SubmitEmptyResult` also supports implementing byzantine participants for testing that submit an empty result too early (intentional protocol violation), *before* the final DKG phase concluded. SubmitEmptyResult must be called strictly after the final phase has ended if DKG has failed.

func (*Client) SubmitParametersAndResult added in v0.39.0

func (c *Client) SubmitParametersAndResult(indexMap flow.DKGIndexMap, groupPublicKey crypto.PublicKey, publicKeys []crypto.PublicKey) error

SubmitParametersAndResult posts the DKG setup parameters (`flow.DKGIndexMap`) and the node's locally-computed DKG result to the DKG white-board smart contract. The DKG results are the node's local computation of the group public key and the public key shares. Serialized public keys are encoded as lower-case hex strings. Conceptually the flow.DKGIndexMap is not an output of the DKG protocol. Rather, it is part of the configuration/initialization information of the DKG. Before an epoch transition on the happy path (using the data in the EpochSetup event), each consensus participant locally fixes the DKG committee 𝒟 including the respective nodes' order to be identical to the consensus committee 𝒞. However, in case of a failed epoch transition, we desire the ability to manually provide the result of a successful DKG for the immediately next epoch (so-called recovery epoch). The DKG committee 𝒟 must have a sufficiently large overlap with the recovery epoch's consensus committee 𝒞 -- though for flexibility, we do *not* want to require that both committees are identical. Therefore, we need to explicitly specify the DKG committee 𝒟 on the fallback path. For uniformity of implementation, we do the same also on the happy path.

type Controller

type Controller struct {
	// The embedded state Manager is used to manage the controller's underlying
	// state.
	Manager
	// contains filtered or unexported fields
}

Controller implements the DKGController interface. It controls the execution of a Joint Feldman DKG instance. A new Controller must be instantiated for every epoch.

func NewController

func NewController(
	log zerolog.Logger,
	dkgInstanceID string,
	dkg crypto.DKGState,
	seed []byte,
	broker module.DKGBroker,
) *Controller

NewController instantiates a new Joint Feldman DKG controller.

func (*Controller) End

func (c *Controller) End() error

End terminates the DKG state machine and records the artifacts.

func (*Controller) EndPhase1

func (c *Controller) EndPhase1() error

EndPhase1 notifies the controller to end phase 1, and start phase 2

func (*Controller) EndPhase2

func (c *Controller) EndPhase2() error

EndPhase2 notifies the controller to end phase 2, and start phase 3

func (*Controller) GetArtifacts

func (c *Controller) GetArtifacts() (crypto.PrivateKey, crypto.PublicKey, []crypto.PublicKey)

GetArtifacts returns our node's private key share, the group public key, and the list of all nodes' public keys (including ours), as computed by the DKG.

func (*Controller) GetIndex

func (c *Controller) GetIndex() int

GetIndex returns the index of this node in the DKG committee list.

func (*Controller) Poll

func (c *Controller) Poll(blockReference flow.Identifier) error

Poll instructs the broker to read new broadcast messages, which will be relayed through the message channel. The function does not return until the received messages are processed.

func (*Controller) Run

func (c *Controller) Run() error

Run starts the DKG controller and executes the DKG state-machine. It blocks until the controller is shutdown or until an error is encountered in one of the protocol phases.

func (*Controller) Shutdown

func (c *Controller) Shutdown()

Shutdown stops the controller regardless of the current state.

func (*Controller) SubmitResult

func (c *Controller) SubmitResult() error

SubmitResult instructs the broker to submit DKG results. It is up to the caller to ensure that this method is called after a succesfull run of the protocol.

type ControllerFactory

type ControllerFactory struct {
	// contains filtered or unexported fields
}

ControllerFactory is a factory object that creates new Controllers for new epochs. Each Controller produced by a factory shares the same underlying Local object to sign broadcast messages, the same tunnel tying it to the MessagingEngine, and the same client to communicate with the DKG smart-contract.

func NewControllerFactory

func NewControllerFactory(
	log zerolog.Logger,
	me module.Local,
	dkgContractClients []module.DKGContractClient,
	tunnel *BrokerTunnel,
) *ControllerFactory

NewControllerFactory creates a new factory that generates Controllers with the same underlying Local object, tunnel and dkg smart-contract client.

func (*ControllerFactory) Create

func (f *ControllerFactory) Create(
	dkgInstanceID string,
	participants flow.IdentitySkeletonList,
	seed []byte,
) (module.DKGController, error)

Create creates a new epoch-specific Controller equipped with a broker which is capable of communicating with other nodes. Participants list must be sorted in canonical order. No errors are expected during normal operations.

type InvalidStateTransitionError

type InvalidStateTransitionError struct {
	From State
	To   State
}

InvalidStateTransitionError happens when an invalid DKG state transition is attempted.

func NewInvalidStateTransitionError

func NewInvalidStateTransitionError(from State, to State) InvalidStateTransitionError

NewInvalidStateTransitionError creates a new InvalidStateTransitionError between the specified states.

func (InvalidStateTransitionError) Error

type Manager

type Manager struct {
	sync.Mutex
	// contains filtered or unexported fields
}

Manager wraps a State with get and set methods

func (*Manager) GetState

func (m *Manager) GetState() State

GetState returns the current state.

func (*Manager) SetState

func (m *Manager) SetState(s State)

SetState sets the state.

type State

type State uint32

State captures the state of an in-progress DKG.

const (
	Init State = iota
	Phase1
	Phase2
	Phase3
	End
	Shutdown
)

func (State) String

func (s State) String() string

String returns the string representation of a State

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL