-
Notifications
You must be signed in to change notification settings - Fork 42
DKG design
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.
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.
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.
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.
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.
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.
The smart contract is responsible for distributed key generation (DKG) is placed at smartcontract/minersc
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
- Phase transition function is executed
- Phase state function is executed
-
PhaseNode.Phase
is incrimented
Data created at MPT
Phase funcs
-
createDKGMinersForContribute
called onStart
-
- Creates list of all miners in
all_miners
- Creates list of all miners in
-
- DKG miner list
dkg_miners
- DKG miner list
-
- Empty sharders list
sharders_keep
- Empty sharders list
-
widdleDKGMinersForShare
called onContribute
-
- Filters DKG miners list
dkg_miners
with loaded miners MPK listminers_mpk
- Filters DKG miners list
-
createMagicBlockForWait
called onPublish
-
- Creates and stores MagicBlock with
sharders_keep
,dkg_miners
,group_share_or_signs
,miners_mpk
and currentPhaseNode
- Creates and stores MagicBlock with
-
- Sets miners MPK list
miners_mpk
with empty set
- Sets miners MPK list
-
- Sets group share or sign
group_share_or_signs
with empty set
- Sets group share or sign
Endpoints
contributeMpk
-
- Adds MPK to miners MPK list
miners_mpk
- Adds MPK to miners MPK list
shareSignsOrShares
-
- Adds group share or signs
group_share_or_signs
- Adds group share or signs
-
- Updates miners MPK list
miners_mpk
with revealed shares
- Updates miners MPK list
View-change protocol implements classical state machine pattern
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()
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.
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
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
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
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
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
Every protocol round VRF share is created.
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
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
- Home
- Introduction
- Architecture
- 0Chain Smart Contracts
- Help