Skip to content

DKG design

Sumit Sardana edited this page Dec 30, 2021 · 7 revisions

DKG design

Overview

Phases

There are 5 phases for the view change:

  • start
  • contribute
  • share
  • publish
  • wait

For all phases of a VC at least one miner and at least one sharder form previous VC set is required and used.

Start

During the start phase the smart contract will get the list of all miners who have registered. The list is sorted by stake and picks N miners to become the dkg miners. If there are more than N miners in the list the sort allows only those with the most staked to be part of the dkg miners. The secondary sorting order is order by ID. It required for hash calculation. Once the list is made, it is saved in the smart contract's mpt. This allows miners who aren't part of the current view change to request the list from the sharders through a rest api. At least one miners from previous set used in next VC set even if its stake is less then stake of N miners. The N is number in range [min_n; max_n] that's configured in sc.yaml (minersc group). If number of registered miners is less then min_n, then DKG can't move on and waits for more miners.

Contribute

Part of the dkg miners list is also the N (max amount of miners), K (threshold needed for dkg), T (threshold need for security). All miners in the dkg miners list use the N and T to create an MPK. This MPK is sent to the smart contract. Any miner who fails to send an MPK during the contribute period is taken out of the dkg miners list. This is done because the MPK will be used to verify the signs during the share phase and the share or signs during the publish phase.

Share

During this phase the miners only communicate with each other. They use the MPKs they sent to the blockchain to derive secret shares for every miner in the dkg miners list and send them the share. When a miner recieves a share they use the published MPK from the miner who sent the share to verify it. Once verified the miner will use the share to sign a message back to the original miner. The original miner collects the message and signature from all the other miners in the dkg miners list. If a miner doesn't recieve a signature they use the secret share instead for that miner.

Publish

Every miner sends the collection of shares or signs to the smart contract. The smart contract verifies the share and signs are correct. If enough shares for one miner come in they are removed from the dkg miners list. Likewise, if a miner doesn't publish the shares or signs then they are also removed from the list.

Wait

At the beginning of this phase a magic block is created for the next view change. Every miner uses the list on the magic block to determine the secret shares used for their personal private key for the VRF in the next view change.

Miners store their DKG secrets and the promoted Magic Block data and send 'wait' transaction to Miner SC to notify it, that the data is stored. If T miners don't send the transaction, then this magic block rejected and VC restarted. Because, miners without DKG secrets can't continue BC.

Miner smart contract

The smart contract is responsible for distributed key generation (DKG) is placed at smartcontract/minersc scheme2

Smart contract implements classical state machine approach for managing current DKG state. Current state is stored in MPT as a PhaseNode structure.

type PhaseNode struct {
	Phase        Phase `json:"phase"`
	StartRound   int64 `json:"start_round"`
	CurrentRound int64 `json:"current_round"`
	Restarts     int64 `json:"restarts"`
}

Every time transition is made PhaseNode.Phase following actions are executed sequentially

  1. Phase transition function is executed
  2. Phase state function is executed
  3. PhaseNode.Phase is incrimented

Data

Data created at MPT

Phase funcs

  • createDKGMinersForContribute called on Start
    • Creates list of all miners in all_miners
    • DKG miner list dkg_miners
    • Empty sharders list sharders_keep
  • widdleDKGMinersForShare called on Contribute
    • Filters DKG miners list dkg_miners with loaded miners MPK list miners_mpk
  • createMagicBlockForWait called on Publish
    • Creates and stores MagicBlock with sharders_keep, dkg_miners, group_share_or_signs, miners_mpk and current PhaseNode
    • Sets miners MPK list miners_mpk with empty set
    • Sets group share or sign group_share_or_signs with empty set

Endpoints

  • contributeMpk
    • Adds MPK to miners MPK list miners_mpk
  • shareSignsOrShares
    • Adds group share or signs group_share_or_signs
    • Updates miners MPK list miners_mpk with revealed shares

View-change protocol

View-change protocol implements classical state machine pattern scheme1

Data

type viewChangeProcess struct {
	shareOrSigns  *block.ShareOrSigns
	mpks          *block.Mpks
	viewChangeDKG *bls.DKG
}
type DKG struct {
	T  int
	N  int
	ID PartyID
	msk []Key
	sij                  map[PartyID]Key
	receivedSecretShares map[PartyID]Key
	Si Key
	Pi *PublicKey
	mpks      []PublicKey
}
type DKGSummary struct {
	datastore.IDField
	StartingRound int64             `json:"starting_round"`
	SecretShares  map[string]string `json:"secret_shares"`
}

DKGProcess

DKGProcess() is the main func running view change protocol on the miners side (miner/protocol_view_change.go). The miner queries sharders every repeat interval via rest api for PhaseNode change (rest api returns current last finalized block version of PhaseNode). If phase state is changed to the next one, state machine transfer is executed. If phase changed was missed and smart contract is more than one stage further DKG process is restarted.

ContributeMpk

node = getDKGMinersFromState(lfb, MINERS_SC_NODE_KEY)
viewChangeProcess.viewChangeDKG =  MakeDKG(node.T, node.N)
SendSmartContractTxn(minersc.ADDRESS, viewChangeProcess.viewChangeDKG.GetMPKs(), node.N2NURLs())
  • gets stored (lfb) miner list from state
  • uses it’s T N to create own DKG
  • stores it to chain as viewChangeDKG structure
  • sends contributeMpk transaction with generated MPKs

SendSijs

mpks = mc.getMinersMpks(lfb)
node = getDKGMinersFromState(lfb, MINERS_SC_NODE_KEY)
dkg = viewChangeProcess.viewChangeDKG
registerMiners(mpks, node)
For every mpk in mpks
	id = bls.ComputeIDdkg(mpk)
	share = ComputeDKGKeyShare(id, dkg)
	viewChangeDKG.AddSecretShare(share)
        sendDKGShare(share, node)
  • gets stored (lfb) MPK list from state
  • gets stored (lfb) miner list from state
  • registers miners
  • for every MPK
    • generate it's id, share
    • store share to chain
    • send shares to miners

ReceiveShare

mpks = viewChangeProcess.mpks
dkg = viewChangeProcess.viewChangeDKG
valid = ValidateShare(dkg, mpk, share)

if valid
    dkg.AddSecretShare(ComputeIDdkg(nodeID), receivedShare, false)
  • receive share via REST API
  • validate secret share with stored MPK
  • store in dkg if valid

PublishShareOrSigns

mpks = mc.getMinersMpks(lfb)
ids = bls.ComputeIDdkg(mpks)
dkg = viewChangeProcess.viewChangeDKG
sos = viewChangeProcess.shareOrSigns = createSOS(dkg, ids)
node = getDKGMinersFromState(lfb, MINERS_SC_NODE_KEY)

signatureScheme = chain.GetServerChain().GetSignatureScheme()
for every share in sos.ShareOrSigns
    signatureScheme.Verify(share.Sign, node.publicKeys, share.Message)

SendSmartContractTxn(minersc.ADDRESS, sos, node.N2NURLs())
  • create share or sign with shares DKGKeyShare((bls.ComputeIDdkg(k))) for every miner MPK
  • verify every share for miners list
  • send share or sign to miners

Wait

magicBlock = mc.GetMagicBlockFromSC(lfb)
dkg = viewChangeProcess.viewChangeDKG
mpks = magicBlock.Mpks.GetMpkMap()

myShare = share.ShareOrSigns[selfNodeKey]
For every key, sos in magicBlock
    validShare = ValidateShare(dkg, MPKs[sos], share)
    if validShare
        AddSecretShare(dkg, bls.ComputeIDdkg(key), myShare)

AggregatePublicKeyShares(dkg, mpks)
AggregateSecretKeyShares(dkg)
StoreDKGSummary(dkg.GetDKGSummary())
StoreMagicBlock(magicBlock)

viewChangeProcess.clearViewChange()
  • load created magic block from smartcontract
  • use stored in chain dkg
  • use stored in magic block mpks
  • validate every share in magic block
  • add this share if valid to dkg
  • aggregate public shares to dkg
  • aggregate secret shares to dkg
  • store DKGSummary to the store for future use in VRFs
  • store magic block to store
  • prepare whole view change structure for future use

VRF

Every protocol round VRF share is created.

SendVRF

mb  = GetMagicBlock(current_round)
message = current_number + timeou_count + last_round_vrf_random_seed
var dkg = mc.GetDKGFromSummary(current_round)
share = GetBlsShare(message, current_round, dkg)
AddVRFShare(current_round, share)

if mb.Miners hasNode sender_node
    SendVRFShare(share)
  • use last magic block
  • create message as concatenation of three fields
  • uses created DKGsummary from view-change protocol to create DKG
  • if miner is from miners list create share
  • send share to all miners from list

AddVRFShare

message = GetBlsMessageForRound(current_round)
partyID = ComputeIDdkg(share.GetPartyID)
dkg     = GetDKG(current_round)
valid = VerifySignature(dkg, share, message, partyID)

if valid 
    AddVRFShare(share, dkg.T)
    
collected = checkThreshold(current_round, dkg.T) //ThresholdNumBLSSigReceived()
if collected 
	previous_round = GetRound(mr.GetRoundNumber() - 1)
        groupSignature = CalBlsGpSign(dkg, share )
        bOutput = Hash(groupSignature)
	computeRoundRandomSeed(previous_round, current_round, rbo) //used to start next round
  • validate received share
  • add share to store
  • check threshold of collected VRFs
  • if collected enough compute random seed
  • use random seed to start next round