1"use strict";(globalThis.webpackChunkdrand_docs=globalThis.webpackChunkdrand_docs||[]).push([[6077],{489(e,n,t){t.r(n),t.d(n,{assets:()=>h,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"concepts/concepts-specification","title":"Protocol Specification","description":"A description of drand\'s protocol","source":"@site/docs/concepts/03-Specification.md","sourceDirName":"concepts","slug":"/docs/specification/","permalink":"/docs/specification/","draft":false,"unlisted":false,"editUrl":"https://github.com/drand/drand-docs/tree/master/docs/concepts/03-Specification.md","tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"id":"concepts-specification","title":"Protocol Specification","description":"A description of drand\'s protocol","slug":"/docs/specification/"},"sidebar":"conceptsSidebar","previous":{"title":"Security Model","permalink":"/docs/security-model/"},"next":{"title":"Timelock Encryption","permalink":"/docs/timelock-encryption/"}}');var s=t(74848),a=t(28453);const o={id:"concepts-specification",title:"Protocol Specification",description:"A description of drand's protocol",slug:"/docs/specification/"},r="Concepts: Specification",h={},d=[{value:"Notations",id:"notations",level:2},{value:"drand node",id:"drand-node",level:3},{value:"drand network",id:"drand-network",level:3},{value:"drand beacon",id:"drand-beacon",level:3},{value:"Beacon ID",id:"beacon-id",level:3},{value:"Metadata",id:"metadata",level:3},{value:"Scheme",id:"scheme",level:3},{value:"Group configuration",id:"group-configuration",level:3},{value:"Group configuration hash",id:"group-configuration-hash",level:4},{value:"Wireformat & API",id:"wireformat--api",level:2},{value:"drand node",id:"drand-node-1",level:2},{value:"drand versioning",id:"drand-versioning",level:2},{value:"drand modules",id:"drand-modules",level:2},{value:"Setup phase",id:"setup-phase",level:3},{value:"Getting the public key of the coordinator",id:"getting-the-public-key-of-the-coordinator",level:4},{value:"Collecting the keys of the participants",id:"collecting-the-keys-of-the-participants",level:4},{value:"Coordinator pushing the new group configuration",id:"coordinator-pushing-the-new-group-configuration",level:4},{value:"Distributed key generation",id:"distributed-key-generation",level:3},{value:"Types of distributed key generation",id:"types-of-distributed-key-generation",level:4},{value:"Network level packets",id:"network-level-packets",level:4},{value:"Phase transitions",id:"phase-transitions",level:4},{value:"Deal phase",id:"deal-phase",level:4},{value:"Response phase",id:"response-phase",level:4},{value:"Justification phase",id:"justification-phase",level:4},{value:"Finish phase",id:"finish-phase",level:4},{value:"Randomness generation",id:"randomness-generation",level:3},{value:"Randomness generation period",id:"randomness-generation-period",level:4},{value:"Beacon: chained or unchained",id:"beacon-chained-or-unchained",level:4},{value:"Root of trust",id:"root-of-trust",level:4},{value:"Catchup mode",id:"catchup-mode",level:4},{value:"Syncing",id:"syncing",level:4},{value:"Cryptographic specification",id:"cryptographic-specification",level:2},{value:"drand curve",id:"drand-curve",level:3},{value:"Distributed public key",id:"distributed-public-key",level:3},{value:"Beacon signature",id:"beacon-signature",level:3},{value:"Partial beacon signature",id:"partial-beacon-signature",level:3},{value:"Distributed key generation",id:"distributed-key-generation-1",level:3},{value:"Input",id:"input",level:4},{value:"Authentication",id:"authentication",level:4},{value:"Setup",id:"setup",level:4},{value:"Deal phase",id:"deal-phase-1",level:4},{value:"Response phase",id:"response-phase-1",level:4},{value:"Justification phase",id:"justification-phase-1",level:4},{value:"Finish phase",id:"finish-phase-1",level:4},{value:"Resharing",id:"resharing",level:3},{value:"Setup",id:"setup-1",level:4},{value:"Distinction of roles",id:"distinction-of-roles",level:4},{value:"Deal phase",id:"deal-phase-2",level:4},{value:"Response phase",id:"response-phase-2",level:4},{value:"Justification phase",id:"justification-phase-2",level:4},{value:"Finish phase",id:"finish-phase-2",level:4}];function l(e){const n={a:"a",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",hr:"hr",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"concepts-specification",children:"Concepts: Specification"})}),"\n",(0,s.jsx)(n.p,{children:"This document is a specification of the drand protocols."}),"\n",(0,s.jsx)(n.h2,{id:"notations",children:"Notations"}
1),"\n",(0,s.jsx)(n.h3,{id:"drand-node",children:"drand node"}),"\n",(0,s.jsx)(n.p,{children:"A drand node is a server that runs the drand code, participates in the\ndistributed key generation (DKG) process and the randomness generation and that can\nreply to public request API. It can instantiate and run multiple independent\ninternal randomness processes, where each of them has its own randomness generation frequency.\nThe following representation is what gets embedded in each group configuration\nfile, one per randomness process. This is what each process knows about other\ndrand nodes on its own network:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"type Node struct {\n Key []byte // public key on bls12-381 G1\n\tAddr string // publicly reachable address of the node\n\tTLS bool // reachable via TLS\n\tSignature []byte \n\tIndex uint32 // index of the node w.r.t. to the network\n}\n"})}),"\n",(0,s.jsx)(n.p,{children:"A node can be referenced by its hash as follows:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"func (n *Node) Hash() []byte {\n\th := hashFunc()\n\t_ = binary.Write(h, binary.LittleEndian, n.Index)\n\t_, _ = n.Identity.Key.MarshalTo(h)\n\treturn h.Sum(nil)\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Public Key"}),": Public keys of drand nodes are points on the G1 group of the\nBLS12-381 curve. See the ",(0,s.jsx)(n.a,{href:"#drand-curve",children:"curve"})," section for more information."]}),"\n",(0,s.jsx)(n.h3,{id:"drand-network",children:"drand network"}),"\n",(0,s.jsx)(n.p,{children:"Each group of nodes that runs a specific process (e.g., one with the same set of parameters) constitutes a separate network.\nEach network will act as an independent beacon generator."}),"\n",(0,s.jsx)(n.h3,{id:"drand-beacon",children:"drand beacon"}),"\n",(0,s.jsxs)(n.p,{children:["A drand beacon is what the drand network periodically creates and that can be\nused to derive the randomness. A beacon contains the signature of the previous\nbeacon generated, the round of this beacon and signature. See the ",(0,s.jsx)(n.a,{href:"#beacon-chain",children:"beacon\nchain"})," section for more information."]}),"\n",(0,s.jsx)(n.h3,{id:"beacon-id",children:"Beacon ID"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"Beacon ID"})," is the unique identifier among existing beacon processes running on a drand node. Thanks to\nthis ID, each drand node can dispatch messages received to the correct internal\nprocess to attend it. The ",(0,s.jsx)(n.code,{children:"Beacon ID"})," is set by the leader when starting a new beacon and stays the same for the lifetime of the process. For backward\ncompatibility reasons, the default id for pre-existing beacon is an empty string. The\nword ",(0,s.jsx)(n.code,{children:"default"})," is reserved as an equivalent."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Note"}),": BeaconID is manually set by operators and is just a regular identifier. In the other hand,\nthe chain hash binds the public key of the network and is therefore a cryptographic identifier,\nand it is needed by clients. Clients don't deal with beacon IDs at all."]}),"\n",(0,s.jsx)(n.h3,{id:"metadata",children:"Metadata"}),"\n",(0,s.jsx)(n.p,{children:"Each request sent by a drand node will contain this field. It is used to hold any important message-related data\nnodes need to communicate to others. The protobuf definition for this field is:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"message Metadata {\n NodeVersion node_version = 1;\n string beaconID = 2;\n bytes chain_hash = 3;\n}\n"})}),"\n",(0,s.jsx)(n.h3,{id:"scheme",children:"Scheme"}),"\n",(0,s.jsx)(n.p,{children:"A scheme is a network-level configuration that a coordinator can set when starting a new network. There\nis fixed quantity of possible values. Each one establishes a different set of parameters which affects\nsome working aspects of the network. If no scheme is set on network starting, a default value is used.\nThe available scheme are:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:'// DefaultSchemeID is the default scheme ID with signatures on G2 on the BLS12-381 curve.\nconst DefaultSchemeID = "pedersen-bls-chained"\n\n// UnchainedSchemeID is the scheme ID used to set unchained randomness on beacons, with signatures on G2.\nconst UnchainedSchemeID = "pedersen-bls-unchained"\n\n// ShortSigSchemeID is the scheme id used to set unchained randomness on beacons with G1 and G2 swapped\n// compared to the original schemes. Deprecated
1in favor of the RFC compliant "bls-unchained-g1-rfc9380" one.\nconst ShortSigSchemeID = "bls-unchained-on-g1"\n\n// SigsOnG1ID is the scheme id used to set unchained randomness on beacons with signatures on G1 that are\n// compliant with the hash to curve RFC.\nconst SigsOnG1ID = "bls-unchained-g1-rfc9380"\n\n// BN254UnchainedOnG1SchemeID is the scheme id used to set unchained randomness on beacons with signatures on G1,\n// on the BN254 curve.\nconst BN254UnchainedOnG1SchemeID = "bls-bn254-unchained-on-g1"\n\n'})}),"\n",(0,s.jsxs)(n.p,{children:["As you can notice, the default value is ",(0,s.jsx)(n.code,{children:"pedersen-bls-chained"}),". This is because of backward-compatibility\nreasons. drand nodes older or equal to version ",(0,s.jsx)(n.code,{children:"1.3.0"}),", which don't know anything about schemes,\nuse this set of parameters."]}),"\n",(0,s.jsx)(n.h3,{id:"group-configuration",children:"Group configuration"}),"\n",(0,s.jsx)(n.p,{children:"Group configuration: A structure that contains all the necessary information\nabout a running drand network:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Nodes: A list of nodes information that represents all nodes on the network."}),"\n",(0,s.jsx)(n.li,{children:"Threshold: The number of nodes that are necessary to participate to a\nrandomness generation round to produce a new random value. Given the security\nmodel of drand, the threshold must be superior to 50% of the number of nodes."}),"\n",(0,s.jsx)(n.li,{children:'Period: The period at which the network creates new random value. Also referred to as "frequency".'}),"\n",(0,s.jsx)(n.li,{children:"ID: The ID which a drand node uses to tag each message in order to run more than one\nbeacon processes (i.e., participate in more than one networks) at the same time."}),"\n",(0,s.jsx)(n.li,{children:"Scheme: The id of the scheme a network uses to define a group of configuration\nvalues related to the randomness generation process."}),"\n",(0,s.jsxs)(n.li,{children:["GenesisTime: An UNIX timestamp in seconds that represents the time at which\nthe first round of the drand chain starts. See the ",(0,s.jsx)(n.a,{href:"#beacon-chain",children:"beacon\nchain"})," section for more information."]}),"\n",(0,s.jsx)(n.li,{children:"GenesisSeed: A generic slice of bytes that is the input for nodes that create\nthe first beacon. This seed is the hash of the initial group configuration, as\nshown below."}),"\n",(0,s.jsx)(n.li,{children:"Distributed public key: A list of points used to verify the partial and final\nbeacons created by the network. This field is nil if the network hasn't ran\nthe setup phase yet. Each point lies on the group G1 of the BLS12-381 curve."}),"\n",(0,s.jsx)(n.li,{children:"TransitionTime: An UNIX timestamp in seconds that represents the time the\nnetwork denoted by this group configuration took over a previous network. This\nfield is empty is the network has never reshared yet. See TODO for more\ninformation."}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Note"}),": This group information is only shared between internal processes of\ndrand nodes. Even though it doesn't expose private key materials it non-essential\ninformation from the point of view of users. A public struct derived from\nthe group to share to clients is described in the ",(0,s.jsx)(n.a,{href:"#root-of-trust",children:"root of trust section"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"group-configuration-hash",children:"Group configuration hash"}),"\n",(0,s.jsx)(n.p,{children:"The group configuration can be uniquely referenced via its canonical hash. The\nhash of the group file is used during a resharing procedure to make sure node\nare resharing from the correct group.\nThe hash is derived using the blake2b hash function. The Go procedure works as\nfollow:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"func (g *Group) Hash() []byte {\n\th := hashFunc()\n\n\tsort.Slice(g.Nodes, func(i, j int) bool {\n\t\treturn g.Nodes[i].Index < g.Nodes[j].Index\n\t})\n\n\t// all nodes public keys and positions\n\tfor _, n := range g.Nodes {\n\t\t_, _ = h.Write(n.Hash())\n\t}\n\n\t_ = binary.Write(h, binary.LittleEndian, uint32(g.Threshold))\n\t_ = binary.Write(h, binary.LittleEndian, uint64(g.GenesisTime))\n\n\tif g.TransitionTime != 0 {\n\t\t_ = binary.Write(h, binary.LittleEndian, g.TransitionTime)\n\t}\n\n\tif g.PublicKey != nil {\n\t\t_, _ = h.Write(g.PublicKey.Hash())\n\t}\n\n\t// To keep backward compatibility\n\tif !common2.IsDefaultBeaconID(g.ID) {\n\t\t_, _ = h.Write([]byte(g.ID))\n\t}\n\n\treturn h.Sum(nil)\n}\n"})}),"\n",(0,s.jsx)(n.h2,{id:"wireformat--api",children:"Wireformat & API"}),"\n",(0,s.jsxs)(n.p,{children:["drand currently uses ",(0,s.jsx)(n.a,{href:"https://grpc.io/",children:"gRPC"})," as the networking protocol. All\nexposed services and protobuf definitions are in the\n",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/master/protobuf/drand/protocol.proto",children:"protocol.proto"}),"\nfile for the intra-nodes protocols and in the\n",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/master/protobuf/drand/api.proto",children:"api.proto"}),"\nfile."]}),"\n",(0,s.jsxs)(n.p,{children:["The DKG related protobuf definitions are in ",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/master/protobuf/dkg/dkg.proto",children:"dkg.proto"}),'\nand the "control" ones are in ',(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/master/protobuf/dkg/dkg_control.proto",children:"dkg_control.proto"})]}
1),"\n",(0,s.jsx)(n.h2,{id:"drand-node-1",children:"drand node"}),"\n",(0,s.jsxs)(n.p,{children:["Generating public randomness is the primary functionality of drand. Public\nrandomness is generated collectively by drand nodes and is made publicly available.\nEach node can run multiple processes (i.e., participate in multiple networks) at the same time, each of which is independent of each other,\nwith its own set of parameters. All processes use the same node address to send messages to\nand the node will identify and dispatch the request to the correct process/network (running\ninternally) using an ID, known as the ",(0,s.jsx)(n.code,{children:"Beacon ID"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"The following diagram explains visually the above operation."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"multi-network-drand-nodes-full.png",src:t(58631).A+"",width:"898",height:"538"})}),"\n",(0,s.jsx)(n.p,{children:'In the above figure, each drand node is running 4 processes, i.e., participates in 4 different randomness generation networks. Each one of a node\'s processes communicates with\nthe corresponding process running in the rest of the nodes, i.e., the process "Network A" in Node 1 communicates with the process "Network A" in nodes 2, 3 and 4, in order to achieve their common goal: generate public randomness.\nIt is worth highlighting that not all nodes need to participate in all networks, i.e., run all existing processes. The following\ndiagram shows this scenario.'}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"multi-network-drand-nodes-partial.png",src:t(38572).A+"",width:"899",height:"548"})}),"\n",(0,s.jsxs)(n.p,{children:["Messages are delivered to the correct internal process by a module that checks the ",(0,s.jsx)(n.code,{children:"Beacon ID"})," inside the request and redirecting the message accordingly."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"beacon-id-dispatching.png",src:t(16285).A+"",width:"957",height:"445"})}),"\n",(0,s.jsx)(n.h2,{id:"drand-versioning",children:"drand versioning"}),"\n",(0,s.jsxs)(n.p,{children:["Each request sent by a drand node will contain the actual drand protocol version that the node is using. drand uses ",(0,s.jsx)(n.a,{href:"https://semver.org",children:"semantic\nversioning"})," as a versioning protocol. This has a clear purpose. Only nodes with same\nMAJOR version will be allowed to communicate with each other. For backward-compatibility reasons,\nthe fallback value will be ",(0,s.jsx)(n.code,{children:"0.0.0"}),", and nodes with this version will always be allowed to communicate\nwith other nodes. The protobuf definition for this field is:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"message NodeVersion {\n uint32 major = 1;\n uint32 minor = 2;\n uint32 patch = 3;\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Notes"}),": The ",(0,s.jsx)(n.code,{children:"NodeVersion"})," is present inside the ",(0,s.jsx)(n.code,{children:"Metadata"})," field."]}),"\n",(0,s.jsx)(n.p,{children:"In the following diagram, we can see:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Node 2 will be able to interact the rest of the nodes."}),"\n",(0,s.jsx)(n.li,{children:"Node 4 will be only able to interact with nodes whose version is 2.X.X. In this case, it cannot communicate with any other node."}),"\n",(0,s.jsx)(n.li,{children:"Nodes 1 and 3 will be only able to interact with nodes whose version is 1.X.X. In this case, they can communicate with each other."}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"drand_versioning.png",src:t(80240).A+"",width:"767",height:"362"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Notes"}
1),": Node 2 is a node where node-versioning feature is not supported. This node has not been updated to the latest version (above 1.3). In this case,\n",(0,s.jsx)(n.code,{children:"Metadata"})," won't be present on requests, so the fallback version other nodes infer is ",(0,s.jsx)(n.code,{children:"0.0.0"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"drand-modules",children:"drand modules"}),"\n",(0,s.jsx)(n.p,{children:"The A drand network is composed of a distributed set of nodes and has two\nphases / modules:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Setup: The nodes perform a distributed key generation (DKG) protocol to create\nthe collective public key and one private key share per node. The participants\nnever see/use the actual (distributed) private key explicitly but instead\nutilize their respective private key shares for the generation of public\nrandomness."}),"\n",(0,s.jsx)(n.li,{children:"Generation: After the setup, the nodes switch to the randomness generation\nmode. Each node periodically broadcasts a partial signature to all the other\nparticipants signed using a t-of-n threshold version of the Boneh-Lynn-Shacham\n(BLS) signature scheme with their respective private key shares. Once any node\n(or third-party observer) has gathered t partial signatures, it can\nreconstruct the full BLS signature, that can be verified against the\ndistributed public key. The public randomness is then simply the hash of that\nsignature, to ensure that there is no bias in the byte representation of the\nfinal output."}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"setup-phase",children:"Setup phase"}),"\n",(0,s.jsxs)(n.p,{children:["To setup a new network, drand uses the notion the of a ",(0,s.jsx)(n.em,{children:"coordinator"})," that\ncollects the public key of the participants, creates the group configuration\nonce all keys are received, push it back to participants and then start the\ndistributed key generation phase. The coordinator is a member of the new group\nby default. At this stage, the coordinator is trusted for setting up the group\nconfiguration and for starting the Distributed Key Generation."]}),"\n",(0,s.jsxs)(n.p,{children:["This setup phase uses the notion of a common ",(0,s.jsx)(n.em,{children:"secret"})," between all participants.\nThat way, only the participants that know the same secret are able to be listed\nin the new group configuration."]}),"\n",(0,s.jsx)(n.h4,{id:"getting-the-public-key-of-the-coordinator",children:"Getting the public key of the coordinator"}),"\n",(0,s.jsxs)(n.p,{children:["Participants needs to know at least the address ",(0,s.jsx)(n.em,{children:"and"})," public key of the coordinator. drand provides a simple gRPC call that returns the public key of the node.\n",(0,s.jsx)(n.strong,{children:"Note"}),": This part is not ",(0,s.jsx)(n.em,{children:"required"}
1),' by the spec but is implemented by drand\nfor simplicity of use. This protocol relies the TOFU ("Trust On First Use")\napproach: the coordinator is trusted for this and the subsequent phase and gives\nus a valid public key. A node/implementation can skip this step if it knows\nalready the public key of the coordinator by another means (out of band, gossip,\netc).',(0,s.jsx)(n.br,{}),"\n","The gRPC endpoint call is:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-protobuf",children:"rpc GetIdentity(IdentityRequest) returns (IdentityResponse);\n\nmessage IdentityRequest {\n Metadata metadata = 1;\n}\nmessage IdentityResponse {\n string address = 1;\n bytes key = 2;\n bool tls = 3 [deprecated = true];\n // BLS signature over the identity to prove possession of the private key\n bytes signature = 4;\n // --------------\n Metadata metadata = 5;\n // We need to specify the scheme name to make sure the key is getting probably decoded on the client side\n string schemeName = 6;\n}\n"})}),"\n",(0,s.jsx)(n.h4,{id:"collecting-the-keys-of-the-participants",children:"Collecting the keys of the participants"}),"\n",(0,s.jsx)(n.p,{children:"Each non-coordinator participant sends their information via a RPC\ncall."}),"\n",(0,s.jsx)(n.h4,{id:"coordinator-pushing-the-new-group-configuration",children:"Coordinator pushing the new group configuration"}),"\n",(0,s.jsxs)(n.p,{children:["Once the coordinator has received the expected number of node informations, then\nhe creates the group configuration (the operator has given the parameters such as\nthreshold and period to the drand logic).\nWhen creating a group from only public keys and addresses of the node, the\n",(0,s.jsx)(n.em,{children:"index"})," of a node is determined by the lexigraphical order of the public keys as\nslice of bytes.\nThe coordinator then pushes the group configuration to the participants via a Proposal."]}),"\n",(0,s.jsx)(n.p,{children:"As soon as a participant receives this information from the coordinator, then he\nmust be ready to accept DKG packets, but he does not start immediately sending\nhis packet. After the coordinator has successfully sent the group to all\nparticipants, he starts sending the first packet of the distributed key\ngeneration. All nodes that receive the first packet of the DKG from the\ncoordinator (or else, due to network shifts) must send their first packet of the\nDKG as well and start the ticker as explained below."}),"\n",(0,s.jsx)(n.h3,{id:"distributed-key-generation",children:"Distributed key generation"}),"\n",(0,s.jsxs)(n.p,{children:["The distributed key generation protocol implements the Joint Feldman protocol,\nbest described from ",(0,s.jsx)(n.a,{href:"https://www.researchgate.net/publication/225722958_Secure_Distributed_Key_Generation_for_Discrete-Log_Based_Cryptosystems",children:"Gennaro's\npaper"}),":"]}),"\n",(0,s.jsxs)(n.blockquote,{children:["\n",(0,s.jsx)(n.p,{children:"Distributed key generation (DKG) is a main component of threshold\ncryptosystems. It allows a
1set of n servers to generate jointly a pair of\npublic and private keys without assuming any trusted party. A DKG may be run\nin the presence of a malicious adversary who corrupts a fraction (or\nthreshold) of the parties and forces them to follow an arbitrary protocol of\ntheir choice."}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:'Note that the nodes that finish the protocol successfully, called "qualified\nnodes" may be a subset of the nodes that started it: there can be nodes offline\nand nodes malicious during the protocol that will get excluded unless they act\naccordingly to the protocol.'}),"\n",(0,s.jsx)(n.h4,{id:"types-of-distributed-key-generation",children:"Types of distributed key generation"}),"\n",(0,s.jsx)(n.p,{children:"drand supports two operations with respect to setting up a distributed key:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Fresh setup: nodes have no prior shares and want to run the protocol from scratch"}),"\n",(0,s.jsxs)(n.li,{children:["Resharing: Resharing enables to ",(0,s.jsx)(n.em,{children:"remove"})," and ",(0,s.jsx)(n.em,{children:"add"}),' new nodes to a group, while\nkeeping the same public facing information, namely the distributed key. There\nis already a first group of nodes A that have ran the DKG protocol and have\nshares of a distributed private key. This group wants to "re-share their\nshares" to a second group of nodes B. Nodes of group B has no prior shares and\nonly the knowledge of the longterm public keys of nodes in group A. After the\nresharing, nodes in group B will be able to use their new shares to produce\nrandomness and nodes in group A will not be able to participate in randomness\ngeneration with the nodes in group B. the Note that a node can be in group A\nand B as well.']}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"The rest of this section highlights the network level operations that are valid\nfor both types of DKG. Even though the main logic is similar for the two types,\nthe cryptography section explains in details the difference between the two."}),"\n",(0,s.jsx)(n.h4,{id:"network-level-packets",children:"Network level packets"}),"\n",(0,s.jsxs)(n.p,{children:["All messages of the DKG have a canonical hash representation and each node signs\nthat hash before sending out the packet, therefore providing authentication of\nthe messages. The signature scheme is the regular BLS signature as explained in\nthe ",(0,s.jsx)(n.a,{href:"#cryptographic-specification",children:"cryptography"})," section."]}),"\n",(0,s.jsxs)(n.p,{children:["We refer to our ",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/master/protobuf/dkg",children:"DKG protobuf"}),"\nfolder for a detailled description of all DKG-related protobuf packets."]}),"\n",(0,s.jsx)(n.h4,{id:"phase-transitions",children:"Phase transitions"}),"\n",(0,s.jsxs)(n.p,{children:["The protocol runs in ",(0,s.jsx)(n.em,{children:"at most"})," 3 phases: ",(0,s.jsx)(n.code,{children:"DealPhase"}),", ",(0,s.jsx)(n.code,{children:"ResponsePhase"})," and\n",(0,s.jsx)(n.code,{children:"JustificationPhase"}),". The ",(0,s.jsx)(n.code,{children:"FinishPhase"})," is an additional local phase where nodes\ncompute their local private share. However, it can finish after the first two\nphases if there is malicious interference or offline nodes during the first\nphase."]}),"\n",(0,s.jsx)(n.p,{children:"The way the protocol transition works is via time-outs. As soon as a node starts\nthe DKG protocol, it starts a ticker that triggers each transition phase.\nExample:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"DKG timeout is set to 30s"}),"\n",(0,s.jsxs)(n.li,{children:["Node 1 starts the DKG at time T, so he is in ",(0,s.jsx)(n.code,{children:"DealPhase"})," and sends its deals\nto every other node."]}),"\n",(0,s.jsxs)(n.li,{children:["Node 1's ticker ticks at time T+30s, and node 1 enters the ",(0,s.jsx)(n.code,{children:"ResponsePhase"})," and\nsends its responses to every other node. Each ",(0,s.jsx)(n.code,{children:"Response"})," can be a complaint or a\nsuccess depending on the deal the node received at the previous step."]}),"\n",(0,s.jsxs)(n.li,{children:["Node 1's ticker ticks at time T+60s, and node 1 enters the\n",(0,s.jsx)(n.code,{children:"JustificationPhase"})," ",(0,s.jsx)(n.em,{children:"if there was no complaint response received"})," OR in\n",(0,s.jsx)(n.code,{children:"FinishPhase"})," otherwise."]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Fast Sync"}),": drand uses a ",(0,s.jsx)(n.em,{children:"fast sync"})," mode that allows to make the setup\nphase proceeds faster at the cost of higher bandwidth usage. Given the\nrelatively low size of the network, the latter is not a concern. The general\nidea is to move to the next step before the ticker kicks in if we received the\nmessages of the phase from all other nodes already. In more details:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["nodes go into the ",(0,s.jsx)(n.code,{children:"ResponsePhase"})," as soon as they received deals from\neverybody else OR when the ticker kicks in."]}),"\n",(0,s.jsxs)(n.li,{children:["nodes go into the ",(0,s.jsx)(n.code,{children:"FinishPhase"}),' as soon as they received "success" responses\nfrom all other nodes (i.e. all deals were correct) OR']}),"\n",(0,s.jsxs)(n.li,{children:["nodes go into the ",(0,s.jsx)(n.code,{children:"JustificationPhase"})," as soon as they received all responses\nfrom all other nodes, where at least one of the responses is a complaint."]}),"\n",(0,s.jsxs)(n.li,{children:["The transition from the ",(0,s.jsx)(n.code,{children:"JustificationPhase"})," to the ",(0,s.jsx)(n.code,{children:"FinishPhase"})," is done\nlocally: when a node received all justifications or when the ticker kicks in,\nthe nodes compute their final share. The beacon chain is starting at a\npre-defined time so it doesn't impact how nodes are handling this last phase."]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"The phases and the respective messages are described
1in more details in the\nfollowing sections."}),"\n",(0,s.jsx)(n.h4,{id:"deal-phase",children:"Deal phase"}),"\n",(0,s.jsxs)(n.p,{children:['In this first phase, nodes sends their "deal" containing their encrypted share\nto the other nodes as well as the public polynomial from which those shares are\nderived. The share is encrypted via ECIES using the public key of the recipient\nshare holder. A node bundles all its deals into a ',(0,s.jsx)(n.code,{children:"DealBundle"})," that is\nsigned."]}),"\n",(0,s.jsxs)(n.p,{children:["Each ",(0,s.jsx)(n.code,{children:"DealBundle"})," is authentificated so a node can know when they received all\nexpected ",(0,s.jsx)(n.code,{children:"DealBundle"}),", one from each node, by looking at the\n",(0,s.jsx)(n.code,{children:"dealer_index"})," field of all ",(0,s.jsx)(n.code,{children:"DealBundle"}),". If that is the case, the node can\ndirectly transition to the ",(0,s.jsx)(n.code,{children:"ResponsePhase"}),". Otherwise, the node needs to wait\nuntil the ticker kicks in for the next timeouts, before entering the\n",(0,s.jsx)(n.code,{children:"ResponsePhase"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"response-phase",children:"Response phase"}),"\n",(0,s.jsxs)(n.p,{children:["In this second phase, each node first process all their deals received during\nthe previous phase. Each nodes then sends a Response for each shares they have\nreceived and ",(0,s.jsx)(n.em,{children:"should"})," have received: if there is a missing share for a node,\nthis node will send a response for it as well. A ",(0,s.jsx)(n.code,{children:"Response"}),' contains both the\n"share holder" index and the "dealer index" as well as a status. If the share\nholder found its share from that dealer invalid, the status is set as a\ncomplaint (',(0,s.jsx)(n.code,{children:"false"}),") and if the share was valid, the status is set as a success\n(",(0,s.jsx)(n.code,{children:"true"}),").\nA node bundles all its responses into a ",(0,s.jsx)(n.code,{children:"ResponseBundle"})," that is signed."]}),"\n",(0,s.jsxs)(n.p,{children:["When a node received all expected ",(0,s.jsx)(n.code,{children:"ResponseBundle"})," from each node OR when the\nticker kicks in, the node decides to which phase to proceed to:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["If all ",(0,s.jsx)(n.code,{children:"Response.status"})," from each ",(0,s.jsx)(n.code,{children:"ResponseBundle"})," are set to true, the node\ncan directly go into the ",(0,s.jsx)(n.code,{children:"FinishPhase"})," and compute their final share."]}),"\n",(0,s.jsxs)(n.li,{children:["If not, then the node needs to go into the ",(0,s.jsx)(n.code,{children:"JustificationPhase"}),"."]}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"justification-phase",children:"Justification phase"}),"\n",(0,s.jsxs)(n.p,{children:['For each "complaint" responses (i.e. ',(0,s.jsx)(n.code,{children:"status == false"}),") whose",(0,s.jsx)(n.code,{children:"dealer_index"})," is\nequal to their index, a node sends a ",(0,s.jsx)(n.code,{children:"Justification"})," packet that contains the\nnon-encrypted share that the share holder should have received. The goal here is\nthat every node will be able to verify the validity of the share now that is\nunencrypted.\nA node bundles all its ",(0,s.jsx)(n.code,{children:"Justifications"})," into a ",(0,s.jsx)(n.code,{children:"JustificationBundle"})," that is\nsigned."]}),"\n",(0,s.jsxs)(n.p,{children:["A node can silently waits until it receives all justification expected or the\nticker kicks in to go into the ",(0,s.jsx)(n.code,{children:"FinishPhase"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"finish-phase",children:"Finish phase"}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.code,{children:"FinishPhase"}),", each node locally look at the shares they received and\ncompute both their final share and the distributed public key. For the DKG to be\nsucessful, there must be at least more than a threshold of valid shares. For\nmore detail, see the ",(0,s.jsx)(n.a,{href:"#cryptographic-specification",children:"cryptography"})," section. Each node must save\nthe group configuration file augmented with the distributed key. This\nconfiguration file is now representative of functional current drand network."]}),"\n",(0,s.jsxs)(n.p,{children:["When a node stored the new group file, it switches to the randomness generation\nprotocol. Given there might be slight time delays, it must already be ready to\naccept packets for this. However, the node must only start generating randomness\nat the time specified in the ",(0,s.jsx)(n.code,{children:"GenesisTime"})," of the group configuration file."]}),"\n",(0,s.jsx)(n.h3,{id:"randomness-generation",children:"Randomness generation"}),"\n",(0,s.jsx)(n.p,{children:'The randomness generation protocol works in its simple form by having each node\nperiodically broadcasts a "partial" signature over a common input. Each nodes\nwaits to receive these partial signatures, and as soon as one has a subset of at\nleast a "threshold" (parameter given in the group configuration file) of those,\nthis node
1can reconstruct the final signature. The final signature is a regular\nBLS signature that can be verified against the distributed public key. If that\nsignature is correct, then the randomness is simply the hash of it:'}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"rand := sha256.Sum256(signature)\n"})}),"\n",(0,s.jsx)(n.p,{children:"Note here that the hash function shown here is simply an example, a suggestion.\nAn application is free to hash the signature using any secure hash function. The\nimportant point is to verify to validity of the signature."}),"\n",(0,s.jsx)(n.h4,{id:"randomness-generation-period",children:"Randomness generation period"}),"\n",(0,s.jsx)(n.p,{children:'The drand network outputs a new random beacon every period and associates a\nbeacon "round" to a specific time. The mapping between a time and a round allows\nto exactly determine the round number for any given time in the past or future.\nThe relation to determine this mapping is as follow:'}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"// Parameters:\n// * now: UNIX timestamp in seconds\n// * genesis: UNIX timestamp in seconds\n// * period: period in seconds\n// Returns:\n// * round: the round number at which the drand network is at\n// * the time at which this round started\nfunc CurrentRound(now, genesis int64, period uint32) (round uint64, time int64){\n\tif now < genesis {\n\t\t// round 0 is the genesis block: signature is the genesis seed\n\t\treturn 0\n\t}\n\tfromGenesis := now - genesis\n\t// we take the time from genesis divided by the periods in seconds, that\n\t// gives us the number of periods since genesis. We add +1 because round 1\n\t// starts at genesis time.\n\tround = uint64(math.Floor(float64(fromGenesis)/period)) + 1\n\ttime = genesis + int64(nextRound*uint64(period.Seconds()))\n\treturn\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Each node starts sending their partial signature for a given round when it is\ntime to do so, according to the above function. Given the threat model, there\nis always enough honest nodes such that the chain advances at the correct speed.\nIn case this is not true at some point in time, please refer to the ",(0,s.jsx)(n.a,{href:"#catchup-mode",children:"catchup\nsection"})," for more information."]}),"\n",(0,s.jsx)(n.h4,{id:"beacon-chained-or-unchained",children:"Beacon: chained or unchained"}),"\n",(0,s.jsx)(n.p,{children:"drand has now the ability to work in two different modes regarding randomness generation: chained or unchained.\nIn the first case, drand binds the different random beacon together so they form a chain of random\nbeacons. Remember a drand beacon is structured as follows:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"type Beacon struct {\n\tRound uint64\n\tPreviousSignature []byte\n\tSignature []byte\n}\n"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.code,{children:"Round"})," is the round at which the beacon was created, as explained in the\nprevious section."]}),"\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.code,{children:"PreviousSignature"})," is the signature of the beacon that was created at\nround ",(0,s.jsx)(n.code,{children:"Round - 1"}),". It is not present if the network is running on unchained mode."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"Signature"})," is the final BLS signature created by aggregating at least\n",(0,s.jsx)(n.code,{children:"Threshold"})," of partial signatures from nodes."]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"This structure makes it so that each beacon created is building on the previous\none therefore forming a randomness chain."}),"\n",(0,s.jsx)(n.p,{children:"In the second case, drand keeps binding the different random beacon together, but this is not strictly necessary. The\nchain integrity won't be required in other to verify a random value generated."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Notes"}),": drand can choose between these two working modes using the ",(0,s.jsx)(n.code,{children:"--scheme"})," flag.\nSee ",(0,s.jsx)(n.a,{href:"#scheme",children:"scheme"})," section for more information."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Partial beacon creation"}),": At each new round, a node creates a ",(0,s.jsx)(n.code,{children:"PartialBeacon"}
1),"\nwith the current round number, the previous signature and the partial signature\nover the messagem with ",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/v1.2.1/chain/store.go#L39-L44",children:"RoundToBytes"}),"\nbeing an 8 bytes fixed length big-endian serializer."]}),"\n",(0,s.jsx)(n.p,{children:'To determine the "current round" and the "previous signature", the node loads it\nlast generated beacon and sets the following:'}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"currentRound = lastBeacon.Round + 1\npreviousSignature = lastBeacon.Signature\n"})}),"\n",(0,s.jsx)(n.p,{children:"It is important to note that the current round may not be necessarily the round\nof the current time. More information in the following section."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Partial beacon broadcast"}),": Each node then calls the following RPC call:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-protobuf",children:"rpc PartialBeacon(PartialBeaconPacket) returns (drand.Empty);\n"})}),"\n",(0,s.jsx)(n.p,{children:"with the following protobuf packets:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-protobuf",children:"message Parti
1alBeaconPacket {\n // Round is the round for which the beacon will be created from the partial\n // signatures\n uint64 round = 1;\n // signature of the previous round - could be removed at some point but now\n // is used to verify the signature even before accessing the store\n bytes previous_signature = 2;\n // partial signature - a threshold of them needs to be aggregated to produce\n // the final beacon at the given round.\n bytes partial_sig = 3;\n //\n Metadata metadata = 4;\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Final beacon creation"}),": For each incoming partial beacon packet, a\nnode must first verify it, using the partial signature verification routine and\nthen stores it in a temporary cache if it is valid. As soon as there is at\nleast a threshold of valid partial signatures, the node can aggregate them to\ncreate the final signature."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"func (v Verifier) DigestMessage(currRound uint64, prevSig []byte) []byte {\n\th := sha256.New()\n\n\tif !v.scheme.DecouplePrevSig {\n\t\t_, _ = h.Write(prevSig)\n\t}\n\t_, _ = h.Write(RoundToBytes(currRound))\n\treturn h.Sum(nil)\n}\n"})}),"\n",(0,s.jsx)(n.p,{children:"As you can notice here, the previous signature is taken into account\nonly if the scheme configuration indicates it."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Validation of beacon and storage"}),": Once the new beacon is created, the node\nverifies its signature, loads the last saved beacon from the database and tries to\nput it on database, applying a series of checks before it:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:'func (a *appendStore) Put(b *chain.Beacon) error {\n\ta.Lock()\n\tdefer a.Unlock()\n\tif b.Round != a.last.Round+1 {\n\t\treturn fmt.Errorf("invalid round inserted: last %d, new %d", a.last.Round, b.Round)\n\t}\n\tif err := a.Store.Put(b); err != nil {\n\t\treturn err\n\t}\n\ta.last = b\n\treturn nil\n}\n\n\nfunc (a *schemeStore) Put(b *chain.Beacon) error {\n\ta.Lock()\n\tdefer a.Unlock()\n\n\t// If the scheme is unchained, previous signature is set to nil. In that case,\n\t// relationship between signature in the previous beacon and previous signature\n\t// on the actual beacon is not necessary. Otherwise, it will be checked.\n\tif a.sch.DecouplePrevSig {\n\t\tb.PreviousSig = nil\n\t} else if !bytes.Equal(a.last.Signature, b.PreviousSig) {\n\t\treturn fmt.Errorf("invalid previous signature")\n\t}\n\n\tif err := a.Store.Put(b); err != nil {\n\t\treturn err\n\t}\n\n\ta.last = b\n\treturn nil\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"There should never be any gaps in the rounds. A node can now expose it to the external API."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.em,{children:"Note"}),": If the network is using on ",(0,s.jsx)(n.strong,{children:"unchained mode"}),", the previous signature will never be saved on the db. It will be\nset to nil before saving the round on the db."]}),"\n",(0,s.jsx)(n.h4,{id:"root-of-trust",children:"Root of trust"}),"\n",(0,s.jsx)(n.p,{children:"In drand, we can uniquely identify a chain of randomness via a tuple of\ninformation:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:'// Info represents the public information that is necessary for a client to\n// verify any beacon present in a randomness chain.\ntype Info struct {\n\tPublicKey kyber.Point `json:"public_key"`\n\tID string `json:"id"`\n\tPeriod time.Duration `json:"period"`\n\tScheme string `json:"scheme"`\n\tGenesisTime int64 `json:"genesis_time"`\n\tGenesisSeed []byte `json:"group_hash"`\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"This information is constant regardless of the network composition: even after a\nresharing is performed, the chain information is constant."}),"\n",(0,s.jsxs)(n.p,{children:["This root of trust is to be given to clients to embed in the application. For\nsimplicity, we also refer to the chain information by its hash of the ",(0,s.jsx)(n.code,{children:"Info"}),"\nstructure:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:'// Hash returns the canonical hash representing the chain information. A hash is\n// consistent throughout the entirety of a chain, regardless of the network\n// composition, the actual nodes, generating the randomness.\nfunc (c *Info) Hash() []byte {\n\th := sha256.New()\n\t_ = binary.Write(h, binary.BigEndian, uint32(i.Period.Seconds()))\n\t_ = binary.Write(h, binary.BigEndian, i.GenesisTime)\n\n\tbuff, err := i.PublicKey.MarshalBinary()\n\tif err != nil {\n\t\tlog.DefaultLogger().Errorw("chain info: failed to hash pubkey", "err", err)\n\t}\n\n\t_, _ = h.Write(buff)\n\t_, _ = h.Write(i.GenesisSeed)\n\n\t// To keep backward compatibility\n\tif !common.IsDefaultBeaconID(i.ID) {\n\t\t_, _ = h.Write([]byte(i.ID))\n\t}\n\n\treturn h.Sum(nil)\n'})}),"\n",(0,s.jsx)(n.h4,{id:"catchup-mode",children:"Catchup mode"}),"\n",(0,s.jsxs)(n.p,{children:["Nodes must have a ticker that kicks in every period of time (started at the\ngenesis time). At each kicks, a node loads its last beacon generated and runs\nthe protocol as shown above. Under normal circumstances, the ",(0,s.jsx)(n.code,{children:"Round"})," field\nshould be the round that corresponds to the current time."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Network halting"}),': However, it may happen that there is not enough partial\nbeacon being broadcasted at one time therefore there will not be any random\nbeacon created for this round. Under these circumstances, nodes can enter a\n"catchup" mode.']}),"\n",(0,s.jsxs)(n.p,{children:["To detect if the network is stalled, each node at each new tick must verify that\nthe ",(0,s.jsx)(n.code,{children:"lastBeacon.Round + 1"}
1)," equals the current round given by their local clock.\nIf that is not the case, that means there wasn't a random beacon generated in\ntime in the previous round OR this node didn't receive enough partial beacons for\nsome reasons."]}),"\n",(0,s.jsxs)(n.p,{children:["If that condition is true, nodes must first try to sync with each other: each node\nasks the other nodes if they have a random beacon at the round ",(0,s.jsx)(n.code,{children:"lastBeacon.Round + 1"}),"\nAND higher beacons as well. If a node receives a valid beacon for the\nrequested round, that means the network is still producing randomness but for\nsome networking reasons, he didn't receive correctly the partial beacons. In\nthis case, if the last beacon received corresponds to the current round, the\nnode must wait on the next tick and continue as usual."]}),"\n",(0,s.jsx)(n.p,{children:"If the sync didn't return any more recent valid beacons, that probably means the\nnetwork is stalled. In that case, nodes must continue to broadcast the same\npartial beacon as usual at every tick."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Network catchup"}),": At some point, there will be enough honest & alive nodes to\nbroadcast their partial signatures such that a new beacon can be aggregated, for\nthe round R. However, that round R does not correspond to the current round\ngiven their local clock. In this situation, each node must produce their partial\nbeacons until the current round as fast as possible. More concretely, each node\nbroadcasts their new partial beacon from the last generated beacon until they\nreach the current round according to the local clock. As soon as a new beacon is\naggregated, nodes look if the round corresponds to the current round, and if\nnot, prepare to broadcast their next partial signatures."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example"}),":"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Period is 30s, genesis time = 10"}),"\n",(0,s.jsx)(n.li,{children:"Beacon for round 1 generated at T = 10"}),"\n",(0,s.jsx)(n.li,{children:"Beacon for round 2 generated at T = 40"}),"\n",(0,s.jsx)(n.li,{children:"Outage of multiple honest nodes for 70s"}),"\n",(0,s.jsx)(n.li,{children:"When the honest nodes are back online, it's time T = 40+70 = 110 + Beacons round 3 (T=70), round 4 (T=100) should have been generated in the meantime"}),"\n",(0,s.jsxs)(n.li,{children:["When the nodes come back online, they try to sync with each other","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:'They all see all nodes only have the beacon round 2 as the "head" of the\nchain'}),"\n",(0,s.jsx)(n.li,{children:'Therefore, they go into "catchup mode"'}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:"Nodes send a partial beacon for round 3 at time T = 100 (+ some delta for\nsyncing)"}),"\n",(0,s.jsxs)(n.li,{children:["As soon as a node has enough partial beacon for round 3, he creates the final\nbeacon for round 4","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"It then moves to the next round automatically, since round 4 is still in the past"}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.li,{children:"All nodes continue on this mode until all have the round 4 as their heads and\nas long the the current time is less than T=130, since that corresponds to\nround 5"}),"\n",(0,s.jsx)(n.li,{children:"All nodes wait for the round 5 as usual."}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"syncing",children:"Syncing"}),"\n",(0,s.jsx)(n.p,{children:"When a drand node is offline, restarted or detects a halt in the chain's\nprogress (previous section), a node should sync to all other nodes in the\nnetwork. A node indicates the last beacon saved in its database and calls the\nfollowing RPC:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-protobuf",children:"rpc SyncChain(SyncRequest) returns (stream BeaconPacket);\n"})}),"\n",(0,s.jsx)(n.p,{children:"with the following protobuf packet:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-protobuf",children:"// SyncRequest is from a node that needs to sync up with the current head of the\n// chain\nmessage SyncRequest {\n uint64 from_round = 1;\n Metadata metadata = 2;\n}
1\n\nmessage BeaconPacket {\n bytes previous_signature = 1;\n uint64 round = 2;\n bytes signature = 3;\n Metadata metadata = 4;\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Client side"}),": For each incoming ",(0,s.jsx)(n.code,{children:"BeaconPacket"}),", the node runs the regular\nbeacon verification routine as usual. The client stops the syncing process\n(closes the RPC call) when the last valid beacon's round returned is equal to\nthe current round."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Server side"}),": For sync request, the node must load the beacon which has the\ngiven round requested and sends back all subsequent beacons until the last one."]}),"\n",(0,s.jsx)(n.h2,{id:"cryptographic-specification",children:"Cryptographic specification"}),"\n",(0,s.jsx)(n.h3,{id:"drand-curve",children:"drand curve"}),"\n",(0,s.jsxs)(n.p,{children:["drand uses the pairing curve\n",(0,s.jsx)(n.a,{href:"https://hackmd.io/@benjaminion/bls12-381",children:"BLS12-381"}),". All points on the curve are sent using the compressed form."]}),"\n",(0,s.jsxs)(n.p,{children:["The implementation that drand uses is located in the\n",(0,s.jsx)(n.a,{href:"https://github.com/drand/bls12-381",children:"bls12-381"})," repo.\n",(0,s.jsx)(n.strong,{children:"Hash to curve"}),":The"," hash-to-curve algorithm as described in\n",(0,s.jsx)(n.a,{href:"https://www.rfc-editor.org/info/rfc9380",children:"RFC-9380"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Groups"}),": This document uses the notation G1 and G2 as commonly used in\npairing equipped curves. The BLS12-381 specification specifies a base point, or\ngenerator for both group, that drand uses."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Scalar"})," A scalar of the field is serialized in 32 bytes in big endian format."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Polynomial"}),": This document refers to polynomials as a list of points lying in\nthe group G1 of BLS12-381. The first point in the list is the free coefficient\nof the polynomial,i.e.:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"f(x) = c_0 + c_1 * x + ... + c_{t-1} * x^{t-1}\n"})}),"\n",(0,s.jsx)(n.p,{children:"Two polynomials can be added in the following way:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"f(x) + g(x) = (f_0 + g_0) + (f_1 + g_1) * x + ...\n"})}),"\n",(0,s.jsx)(n.h3,{id:"distributed-public-key",children:"Distributed public key"}),"\n",(0,s.jsx)(n.p,{children:"The distributed public key is a list of BLS12-381 G1 points that represents the\npublic polynomial created during the DKG protocol."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"// DistPublic represents the distributed public key generated during a DKG. This\n// is the information that can be safely exported to end users verifying a\n// drand signature. It is the list of all commitments of the coefficients of the\n// private distributed polynomial.\ntype DistPublic struct {\n\t// points on the BLS12-381 G1 curve\n\tCoefficients [][]byte\n}\n"})}),"\n",(0,s.jsx)(n.p,{children:"The coefficients are needed to verify partial beacon signatures.\nThe first coefficient is needed to verify the final beacon signature."}),"\n",(0,s.jsx)(n.h3,{id:"beacon-signature",children:"Beacon signature"}),"\n",(0,s.jsxs)(n.p,{children:["A beacon signature is a regular ",(0,s.jsx)(n.a,{href:"https://www.iacr.org/archive/asiacrypt2001/22480516.pdf",children:"BLS signature"})," over the message:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"func (v Verifier) DigestMessage(currRound uint64, prevSig []byte) []byte {\n\th := sha256.New()\n\n\tif !v.scheme.DecouplePrevSig {\n\t\t_, _ = h.Write(prevSig)\n\t}\n\t_, _ = h.Write(RoundToBytes(currRound))\n\treturn h.Sum(nil)\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["with ",(0,s.jsx)(n.a,{href:"https://github.com/drand/drand/blob/v1.2.1/chain/store.go#L39-L44",children:"RoundToBytes"}),"\nbeing an 8 bytes fixed length big-endian serializer. As the function shows, the beacon signature\nuses the previous signature as input or not depending on the scheme the network has set.\nSee ",(0,s.jsx)(n.a,{href:"#scheme",children:"scheme"})," section for more information."]}),"\n",(0,s.jsx)(n.p,{children:"The ciphersuite used is:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:'var Domain = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_")\n'})}),"\n",(0,s.jsx)(n.p,{children:"A beacon signature can be verified using the first coefficient of the distributed public key\nstored
1in the group configuration:"}),"\n",(0,s.jsx)(n.h3,{id:"partial-beacon-signature",children:"Partial beacon signature"}),"\n",(0,s.jsx)(n.p,{children:"A partial beacon signature is created over the same input as the beacon\nsignature. However, the node's index is prefixed on the first two bytes to the\nsignature."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"func concatenate(signature []byte, index uint16) []byte {\n\tvar buffer bytes.Buffer\n\tbinary.Write(buffer, binary.BigEndian, index)\n\tbuffer.Write(sig)\n\treturn buffer.Bytes()\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Verifying"}),": The public key to validate a partial signature is derived from\nthe distributed public key. Namely, the public key is the evaluation of the\npolynomial whose coefficients are the points of the distributed key, at the\nindex of the signer + 1. We need to increment the signer's index since the\nevaluation point 0 corresponds to the public key but no nodes posseses the\nprivate key. More on that in the DKG sections below.\nTo evaluate the polynomial, one can use the following routine:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-go",children:"// i is the index of the signer\n// commits are the points of the distributed public key\nfunc Eval(i int,commits []Point) Point {\n\txi := Scalar().SetInt64(1 + int64(i)) // x-coordinate of this share\n\tv := Point().Null()\n\tfor j := p.Threshold() - 1; j >= 0; j-- {\n\t\tv.Mul(xi, v)\n\t\tv.Add(v, commits[j])\n\t}\n\treturn v\n}\n"})}),"\n",(0,s.jsx)(n.h3,{id:"distributed-key-generation-1",children:"Distributed key generation"}),"\n",(0,s.jsx)(n.p,{children:"This sections presents the cryptographic operations and safety checks each node\nmust perform during the three phases of the DKG."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Notation"}),': For the sake of readability, this section uses the term "dealer"\nto designate a node that produces a share and a "share holder" to designate a\nnode that receives a share from a dealer. In the case of a fresh DKG, a node is\na both a dealer and a share holder at the same time.']}),"\n",(0,s.jsx)(n.h4,{id:"input",children:"Input"}),"\n",(0,s.jsx)(n.p,{children:"The input are:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"The list of public keys of all participants"}),"\n",(0,s.jsx)(n.li,{children:"The longterm private key of the node"}),"\n",(0,s.jsx)(n.li,{children:"The designated index of this node"}),"\n",(0,s.jsx)(n.li,{children:"The threshold to use during the DKG"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"For a fresh distributed key generation, the index of each node is set as the\nindex of its public key in the list of participants."}),"\n",(0,s.jsx)(n.h4,{id:"authentication",children:"Authentication"}),"\n",(0,s.jsxs)(n.p,{children:["Each packet of each phases is authenticated using a regular BLS signature with\nthe private key of the issuer of the packet over the hash of the packet. You can\nfind the complete description of the packets in the ",(0,s.jsx)(n.a,{href:"#appendix-a-dkg-packets",children:"Appendix A"}),"."]}),"\n",(0,s.jsx)(n.h4,{id:"setup",children:"Setup"}),"\n",(0,s.jsx)(n.p,{children:"Each node creates the following two polynomials as a setup to the first phase:"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Private Polynomial"}),":In"," order to create the shares, Each node locally creates\nrandom polynomial with a ",(0,s.jsx)(n.code,{children:"threshold"})," number of coefficients. In other words, the\nnode creates a list of size ",(0,s.jsx)(n.code,{children:"threshold"})," of random scalars from the prime order\nfield of the BLS12-381 curve."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Public Polynomial"}),": Each nodes compute then the public commitment of this\npolynomial simply by multiplying each coefficients with the base points of G1."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Share Status"}),': Each node must maintain a matrix of "status" of each share\ndistributed by each dealer. A status is either "valid" or "invalid". At the\nend of the protocol, all dealers whose all shares are marked as valid in this\nmatrix are qualified to be in the group. For a fresh DKG, this matrix is a NxN\nmatrix since there are as many dealers as share holders.\nThe matrix must be initialized as having only incorrect shares for all dealers\nat the beginning. This effectively forces all share holders to explicitely\nbroadcast that the shares they receives are correct.']}),"\n",(0,s.jsx)(n.h4,{id:"deal-phase-1",children:"Deal phase"}),"\n",(0,s.jsxs)(n.p,{children:['During this phase, each node must create a valid encrypted "share" to each other\nnode. A share is simply the evaluation of the private pol
1ynomial at the index of\nthe recipient target node. To encrypt the share, one use the ECIES encryption\nalgorithm described ',(0,s.jsx)(n.a,{href:"#ecies",children:"below"})," in the document, with the public key of the\nshare holder and the share serialized as in described in the curve section.\nA pseudo algorithm describes the operation:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'// Node running the following has the following properties:\n// - its index as a dealer (in the group A) DealerIndex di,\n// - a flag "isHolder" denoting if the node is a member of the group B\n// - its index as a share holder ShareIndex si - if isHolder is true\nshares = []\nfor n in shareHolders:\n - share = private_polynomial.Eval(n.Index)\n - mark as valid the share ShareIndex for the dealer DealerIndex in the status matrix\n - if shareHolder && n.Index == ShareIndex\n - continue to next node\n - encryption = ECIES(node.PublicKey, share)\n - append(shares, { encrypted: encryption, index: n.Index })\nreturn shares\n'})}),"\n",(0,s.jsxs)(n.p,{children:["After generating the encrypted shares, each node attaches their public\npolynomial to the encrypted shares in the same packet ",(0,s.jsx)(n.code,{children:"DealBundle"}),". Then each\nnode must signs the packet and embeds the signature in a ",(0,s.jsx)(n.code,{children:"AuthDealBundle"}),"\npacket."]}),"\n",(0,s.jsx)(n.h4,{id:"response-phase-1",children:"Response phase"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Processing of the shares"}),":\nAt the beginning of this phase, the node must first process all published deals\nduring the previous phase.\nThe logic is as follow:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'For each deal bundle:\n - check signature of the packet\n - if invalid, pass to next bundle\n - check if the dealer index is one index in the group\n - if false, pass to the next bundle\n - check if polynomial exists\n - if false, pass to the next bundle\n - check if polynomial is of length "threshold"\n - if false, mark all dealer\'s shares as invalid\n - STORE the polynomial from this dealer for future usage\n - For each share inside the bundle:\n - check if share index is an index in the group\n - if false, pass to next deal\n - check if share index is equal to our node\'s index\n - if false, pass to next share\n - try decrypting the share with the node\'s public key\n - if decryption fails, pass to the next deal\n - evaluate the public polynomial in the bundle to the index of the share\n - expectedCommitShare = publicPoly.Eval(share.Index)\n - evaluate a commitment of the share, by multiplying it with the base point\n - commitShare = share * G1.Base\n - check if "expectedCommitShare == commitShare"\n - if true, mark as valid the share "share.ShareIndex" from the dealer\n "bundle.DealerIndex"\n - STORE the share from this dealer for future usage\n'})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Creation of the responses"}),":"]}),"\n",(0,s.jsxs)(n.p,{children:["Each node sends a response for each of the shares he has or should have\nreceived. In other words, each node at index i looks at all share's index i for\nall dealers and create a response with the same status. Each node bundles these\nresponses into a ",(0,s.jsx)(n.code,{children:"ResponseBundle"}),", signs it and wraps it into a\n",(0,s.jsx)(n.code,{children:"AuthResponseBundle"})," and broadcasts that packet."]}),"\n",(0,s.jsx)(n.h4,{id:"justification-phase-1",children:"Justification phase"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Processing of the responses"}),": For each responses received, each node sets the\nstatus of the share index from the dealer index as designated in the response."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'For each response bundle "bundle":\n - check if the bundle.ShareIndex is a valid index in the group\n - if false, set all share\'s of that ShareIndex as invali
1d\n - continue to next bundle\n - for each response "resp"\n - check if the resp.DealerIndex is a valid index in the group\n - if false, set all share\'s of that ShareIndex as invalid\n - continue to next bundle\n - set matrix[bundle.DealerIndex][resp.ShareIndex] = resp.Status\n'})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Deciding upon next phase"}),": There is a simple rule to decide if a node can go\ninto the ",(0,s.jsx)(n.code,{children:"FinishPhase"}),' already at this point: If all shares of all dealers are\n"valid", then the node can go into the ',(0,s.jsx)(n.code,{children:"FinishPhase"}),". Otherwise, a node needs to\nrun the following logic."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Validating justifications"}),": Each node runs the following logic on all\nreceived ",(0,s.jsx)(n.code,{children:"AuthJustifBundle"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'For each bundle:\n - check if the bundle.DealerIndex is one index in the group\n - if false, pass to next bundle\n - check if the signature is valid,\n - if false, mark all shares of the dealer bundle.DealerIndex invalid\n - check if we have a public polynomial received in the DealPhase from this\n dealer\n - if not, pass to next bundle\n - if yet, save it as "public"\n - For each justifications from that dealer:\n - check if the justification.ShareIndex is in the group\n - if false, mark all shares of the dealer bundle.DealerIndex invalid\n - evaluate the public polynomial at the justification.ShareIndex\n - expected = public.Eval(justification.ShareIndex)\n - evaluate the commitment of the justification.Share\n - seen = justification.Share * G1.Base()\n - check if expected == seen\n - if true, mark as valid the share "justification.ShareIndex" of the\n dealer "bundle.DealerIndex"\n - if true, save the share for later usage\n - if false, pass to next justification\n'})}),"\n",(0,s.jsxs)(n.p,{children:["After processing the justifications, each node can pass into the ",(0,s.jsx)(n.code,{children:"FinishPhase"}),"\nlogic."]}),"\n",(0,s.jsx)(n.h4,{id:"finish-phase-1",children:"Finish phase"}),"\n",(0,s.jsx)(n.p,{children:"In the finish phase, each node locally computes their final share and the\ndistributed public key. At the end, each node can distribute the public key and\nuse the share to create partial beacon.\nThe logic is as follows:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"// INPUT: i: node's index\n// OUTPUT: (share, distributedkey)\n// scalar in the BLS12-381 curve\nfinalShare :Scalar\n// polynomial with coefficients on G1 on the BLS12-381 curve\nfinalPublic :Polynomial\nFor each dealer index:\n - check if all shares from that dealer are marked as valid\n - if false, pass to next dealer\n - add the dealer's share with the index equal to the node's index to \"scalar\"\n - scalar += dealer.shares[node.index]\n - add the dealer's public polynomial to the \"finalPublic\"\n - finalPublic = finalPublic + dealer.public\nreturn (finalShare,finalPublic)\n"})}),"\n",(0,s.jsx)(n.h3,{id:"resharing",children:"Resharing"}),"\n",(0,s.jsxs)(n.p,{children:["Resharing is a mechanism that allows an established group to give ",(0,s.jsx)(n.em,{children:"new"})," shares\nto a ",(0,s.jsx)(n.em,{children:"new"})," group of nodes such that:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"the new group of nodes can now uses their share to produce partial beacon\nsignatures"}),"\n",(0,s.jsx)(n.li,{children:"the old shares can not be validated anymore within the new group"}),"\n",(0,s.jsx)(n.li,{children:"the distributed public polynomial changes but not the free coefficient which\nis the public key used to verify a random beacon"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"There are few differences in the logic w.r.t to a fresh DKG as explained in the\nprevious section."}),"\n",(0,s.jsx)(n.h4,{id:"setup-1",children:"Setup"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Private pol
1ynomial"}),": Each dealer creates its private polynomial from the\n",(0,s.jsx)(n.strong,{children:"share of the node"})," that the node generated in group A. Namely, the free\ncoefficient of the private polynomial is the share:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"f(x) = share + c_1 * x + ... + c_{t-1} * x^{t-1}\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"NOTE"}),":The"," length of the polynomial is set to the threshold of the new group, of the\ngroup B."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Public polynomial"}),": This is created the same way as in the fresh dkg, by\ncommiting the private polynomial."]}),"\n",(0,s.jsx)(n.h4,{id:"distinction-of-roles",children:"Distinction of roles"}),"\n",(0,s.jsx)(n.p,{children:"In the resharing case, there is a group A and a group B of nodes which can be\ncompletely disjoint. Each node in group A has a specific index and each node in\ngroup B has another specific index for that group. We say the group A wants to\nreshare to group B. In that case, the nodes in the group A are the dealers and\nthe nodes in the group B are the share holders. Dealers are the nodes producing\nthe deals and justifications. Share holders are the nodes producing the\nresponses and the final shares at the end of the protocol. The matrix that was\npresented in the regular DKG section is now a matrix NxM where N is the number\nof dealers and M in the number of share holders."}),"\n",(0,s.jsx)(n.p,{children:"Note a node can now have two indexes if it belongs to the group A and group B.\nWe call theses indexes DealerIndex and ShareIndex as consistent with the\nprevious notation in the DKG case."}),"\n",(0,s.jsx)(n.h4,{id:"deal-phase-2",children:"Deal phase"}),"\n",(0,s.jsx)(n.p,{children:"The deal phase is essentially the same except for the index where the node\nevaluate the private polynomial. A dealer evaluates its private polynomial on\nthe indexes of the share holders."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'// Node running the following has the following properties:\n// - its index as a dealer (in the group A) DealerIndex di,\n// - a flag "isHolder" denoting if the node is a member of the group B\n// - its index as a share holder ShareIndex si - if isHolder is true\nshares = []\nfor n in shareHolders:\n - share = private_polynomial.Eval(n.Index)\n - mark as valid the share ShareIndex for the dealer DealerIndex in the status matrix\n - if shareHolder && n.Index == ShareIndex\n - continue to next node\n - encryption = ECIES(node.PublicKey, share)\n - append(shares, { encrypted: encryption, index: n.Index })\nreturn shares\n'})}),"\n",(0,s.jsx)(n.h4,{id:"response-phase-2",children:"Response phase"}),"\n",(0,s.jsx)(n.p,{children:"There are two main differences with respect to the responses phase in a fresh\ndkg:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"A node must be able to verify that the free coefficient of the public\npolynomial of the dealer is the same as the commitment of the share of the\ndealer. That check ensures that the dealer is indeed creating a private\npolynomial from its share and not from a random scalar."}),"\n",(0,s.jsx)(n.li,{children:"A node that is only in group A, i.e. a node that is leaving the network,\ndoesn't need to process the deals at all (since he is not gonna be part of\nthe new network, no shares is meant for it)."}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Processing of the shares"}),":\nAt the beginning of this phase, the node must first process all published deals\nduring the previous phase.\nThe logic is as follow:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'// INPUT:\n// - public polynomial of the current group A: polyA\n// - deals bundle\n// - node ShareIndex\nFor each bundle:\n - check signature of the packet\n - if invalid, pass to next bundle\n - check if the dealer index is one index in the group\n - if false, pass to the next bundle\n - check if polynomial "public" in the bundle exists\n - if false, pass to the next bundle\n - check if polynomial is of length "threshold"\n - if false, mark all dealer\'s shares as invali
1d\n - STORE the polynomial from this dealer for future usage\n - For each share inside the bundle:\n - check if share index is an index in the group\n - if false, pass to next deal\n - check if share index is equal to our node\'s index\n - if false, pass to next share\n - try decrypting the share with the node\'s public key\n - if decryption fails, pass to the next deal\n\n # checking if share is consistent with polynomial\n - evaluate the public polynomial in the bundle at the index of the share\n - expectedCommitShare = public.Eval(share.Index)\n - evaluate a commitment of the share, by multiplying it with the base point\n - commitShare = share * G1.Base\n - check if "expectedCommitShare == commitShare"\n - if false, pass to the next deal\n\n # checking if share is consistent with share of dealer from polynomial of\n # group A\n - dealShareCommit = polyA.Eval(bundle.DealerIndex)\n - expShareCommit = public.Eval(0)\n if dealShareCommit == expShareCommit\n - mark as valid the share "share.ShareIndex" from the dealer "bundle.DealerIndex"\n - STORE the share from this dealer for future usage\n'})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Creation of the responses"}),": In this setting, the ShareIndex field must be\nfilled with the index of the share holder index, whereas in the fresh DKG, the\nindex is the same as DealerIndex."]}),"\n",(0,s.jsx)(n.h4,{id:"justification-phase-2",children:"Justification phase"}),"\n",(0,s.jsx)(n.p,{children:"At this point, nodes that are only in group A and not in group B, i.e. leaving\nthe network can abort the protocol now: their contribution is not needed\nanymore. For the rest of the nodes, the steps follow mostly the same logic:"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Processing of the responses"}),": This step is exactly the same as in the fresh\nDKG case."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Deciding upon next phase"}),": It is the same step as in the fresh DKG case."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Validating justifications"}),': The logic is mostly the same, except that each\nnode needs also to validate that the dealer is indeed "re-sharing its share":\nthe dealer used the commitment of its share as the free coefficient of the\npublic polynomial he advertised during the deal phase.']}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:'// INPUT:\n// - polyA: public polynomial of the group A\n// - list of response bundles\nFor each bundle:\n - check if the bundle.DealerIndex is one index in the group\n - if false, pass to next bundle\n - check if the signature is valid,\n - if false, mark all shares of the dealer bundle.DealerIndex invalid\n - check if we have a public polynomial received in the DealPhase from this\n dealer\n - if not, pass to next bundle\n - if yet, save it as "public"\n - For each justifications from that dealer:\n - check if the justification.ShareIndex is in the group\n - if false, mark all shares of the dealer bundle.DealerIndex invalid\n - evaluate the public polynomial at the justification.ShareIndex\n - expected = public.Eval(justification.ShareIndex)\n - evaluate the commitment of the justification.Share\n - seen = justification.Share * G1.Base()\n - check if expected == seen\n - if false, pass to next justification\n\n # Resharing check\n - dealShareCommit = polyA.Eval(bundle.DealerIndex)\n - expShareCommit = public.Eval(0)\n if dealShareCommit == expShareCommit\n - STORE the share from this dealer for future usage\n - mark as valid the share "justification.ShareIndex" of the\n dealer "bundle.DealerIndex"\n'})}),"\n",(0,s.jsx)(n.h4,{id:"finish-phase-2",children:"Finish phase"}),"\n",(0,s.jsx)(n.p,{children:"The finish phase in case of a resharing is where the the major differences rises\nwith respect to a fresh DKG protocol."}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"On the algebra side, instead of simply summing up valid shares received, each\nnode has to interpolate (using Lagrange interpolation) the valid shares\nreceived to be able to construct a new share. That is valid too for the public\npolynomials received: each node needs to interpolate the coefficients of all\nvalid polynomials column-wise."}),"\n",(0,s.jsx)(n.li,{children:"On the protocol side, each node make sure to output a qualified set of nodes\nfrom only the share holders that replied correctly during the response phases."}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Aggregation of the shares"}),": In a resharing context, each node needs to treat the\nvalid shares they received as the evaluation points of a polynomial. The final\nshare of the node is the secret
1coefficient of that polynomial. The logic is as\nfollows:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"// INPUT\n// - list of shares stored (dealerIndex, value)\n// - ShareIndex: node index\n// - threshold parameter of the group A\n// OUTPUT:\n// - final share of the node\n//\n// temp_points is a list of tuples (x_i,y_i) evaluation points of a polynomial\ntemp_points = []\nFor each share:\n - check if all shares for share.dealerIndex are marked as valid\n - if false, pass to next share\n - append(temp_shares, (share.dealerIndex, value))\n\nSort temp_points by the index of the dealers\ntemp_points = temp_points[0:threshold]\nprivate_poly = interpolation of the polynomial using Lagrange interpolation on the temp_points\nfinalShare = private_poly.Eval(0)\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Aggregation of the public polynomials"}),": In a resharing context, each node\nneeds to treat all valid public polynomials as a matrix where each row is one\nvalid public polynomial. Each node needs to interpolate the public coefficients\ncolumn-wise of that matrix to create one by one the public coefficients of the\nnew public polynomial. The first coefficient is still the same as the previous\ngroup; in other words, the distributed key doesn't change."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"// INPUT:\n// - thresholdA: threshold of the group A\n// - thresholdB: threshold of the group B\n// - all valid public polynomials (dealerIndex, polynomial)\n// - each public polynomial is of length thresholdB\n//\n// OUTPUT:\n// - public polynomial of the group B\n//\n\n// list of coefficients that will form the new public polynomial\nnew_coeffs = []\nFor i in 0..thresholdB:\n // list of evaluation points (index, point on G1)\n tmp_coeffs = []\n for each polynomials p:\n - append(tmp_coeffs, p.Coefficient(i)\n\n sort(tmp_coeffs) via indexes\n # only take the first thresholdA points since the groupA is running with\n # this threshold so only a thresholdA number of nodes a necessary\n tmp_coeffs = tmp_coeffs[0:thresholdA]\n tmp_poly = Lagrange interpolation of the tmp_coeffs pairs\n\n # get the free coefficient of that interpolated polynomial\n append(new_coeffs, temp_poly.Eval(0)\nreturn new_coeffs\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Qualified nodes selection"}),": We need to augment the normal selection rule from\nthe fresh DKG case with a new rule to exclude absent new nodes from group B."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"// INPUT:\n// - status matrix\n// - newNodes: list of nodes indexes in group B\n// OUTPUT:\n// - indexes of the qualified nodes in group B\n\nvalid_dealers = []\nquals = []\nFor each dealer in statuses:\n - check all shares are marked as valid for that dealer in statuses\n - if true, append(valid_dealers, dealer)\n\nFor each node in newNodes:\n - isValid = true\n - for each dealer in valid_dealers:\n - check if share's index node.Index from dealer dealer.Index is valid\n - if not, mark isValid as false\n - if isValid is true\n - append(quals, node.Index)\n\nreturn quals\n"})}),"\n",(0,s.jsx)(n.hr,{})]})}function c(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},16285(e,n,t){t.d(n,{A:()=>i});const i=t.p+"assets/images/beacon_id_dispatching-6b8a67850c1ac02a77c742dfba9267a6.png"},58631(e,n,t){t.d(n,{A:()=>i});const i=t.p+"assets/images/drand_multinetwork_1-902f5c6e5930b3465c197a9bb1b5ff07.png"},38572(e,n,t){t.d(n,{A:()=>i});const i=t.p+"assets/images/drand_multinetwork_2-be022f4eb02fc128680f6384474225dd.png"},80240(e,n,t){t.d(n,{A:()=>i});const i=t.p+"assets/images/drand_versioning-a001e2d54ed24edd73ef4728dee89717.png"},28453(e,n,t){t.d(n,{R:()=>o,x:()=>r});var i=t(96540);const s={},a=i.createContext(s);function o(e){const n=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.