Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bootstrap] Extracted rootblock and updated finalize #1371

Merged
merged 33 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
858d909
Extracted rootBlock command from finalize command for bootstrap util.…
durkmurder Sep 28, 2021
95d6a16
Implemented QC creation as part of finalizing cmd. Updated docs. Impl…
durkmurder Sep 28, 2021
ce49da7
Fixed typo, updated tests
durkmurder Sep 28, 2021
e0e0061
Updated QC building logic. Fixed signing of QC
durkmurder Sep 28, 2021
a125806
Updated godoc
durkmurder Sep 28, 2021
221a262
Updated comments. Reverted last commit
durkmurder Sep 28, 2021
cd65783
Updated how root block is created. Separated root block and votes
durkmurder Sep 28, 2021
0c7b8b9
Added encoding of full DKG to construct QC in finilize step
durkmurder Sep 28, 2021
84a4c79
Merge branch 'master' into yurii/5891-update-finalize
Sep 28, 2021
fb25b62
Updated reading of votes, dkg data in finalizer. Simplified logic for…
durkmurder Sep 28, 2021
fdbd24c
Merge branch 'yurii/5891-update-finalize' of https://github.com/onflo…
durkmurder Sep 28, 2021
567811b
Updated tests
durkmurder Sep 28, 2021
a58bb18
Fixed tests
durkmurder Sep 28, 2021
8757ea6
Linted. Fixed tests
durkmurder Sep 28, 2021
f73b5af
Update cmd/bootstrap/run/qc.go
Sep 28, 2021
175116b
renamed methods for reading node infos to reflect that fact that we a…
Sep 28, 2021
503e8ea
consolidated consistency checks
Sep 28, 2021
e011135
cleanup
Sep 28, 2021
e0de23c
format
jordanschalm Sep 28, 2021
5def7f1
update localnet/integration tests to generate votes separately
jordanschalm Sep 28, 2021
5bee68e
put votes in same directory
jordanschalm Sep 28, 2021
aded4bc
fix path join
jordanschalm Sep 28, 2021
5b1e0bd
Merge branch 'master' into yurii/5891-update-finalize
Sep 29, 2021
77138ac
pass in identities separate from participant data
jordanschalm Sep 29, 2021
80e41b4
Merge branch 'yurii/5891-update-finalize' of github.com:onflow/flow-g…
jordanschalm Sep 29, 2021
44d1fc1
add log
jordanschalm Sep 29, 2021
461a0ef
unwrap encodable type of key for partners
jordanschalm Sep 29, 2021
a404fe0
fix test (lint)
jordanschalm Sep 29, 2021
389a43a
LInted and fixed unit tests
durkmurder Sep 29, 2021
f7cf0b8
Fixed network test
durkmurder Sep 29, 2021
2476f96
Merge branch 'master' of https://github.com/onflow/flow-go into yurii…
durkmurder Sep 29, 2021
c805a2f
Updated godoc
durkmurder Sep 29, 2021
a1d2b2b
Merge branch 'master' into yurii/5891-update-finalize
zhangchiqing Oct 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 15 additions & 19 deletions cmd/bootstrap/cmd/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ import (
"github.com/onflow/flow-go/model/flow/filter"
)

// Checks constraints about the number of partner and internal nodes.
// Internal nodes must comprise >2/3 of consensus committee.
// Internal nodes must comprise >2/3 of each collector cluster.
func checkConstraints(partnerNodes, internalNodes []model.NodeInfo) {

partners := model.ToIdentityList(partnerNodes)
internals := model.ToIdentityList(internalNodes)
all := append(partners, internals...)

// ensureUniformNodeWeightsPerRole verifies that the following condition is satisfied for each role R:
// * all node with role R must have the same weight
func ensureUniformNodeWeightsPerRole(allNodes flow.IdentityList) {
// ensure all nodes of the same role have equal stake/weight
for _, role := range flow.Roles() {
withRole := all.Filter(filter.HasRole(role))
withRole := allNodes.Filter(filter.HasRole(role))
expectedStake := withRole[0].Stake
for _, node := range withRole {
if node.Stake != expectedStake {
Expand All @@ -28,16 +22,18 @@ func checkConstraints(partnerNodes, internalNodes []model.NodeInfo) {
}
}
}
}

// check consensus committee Byzantine threshold
partnerCONCount := partners.Filter(filter.HasRole(flow.RoleConsensus)).Count()
internalCONCount := internals.Filter(filter.HasRole(flow.RoleConsensus)).Count()
if internalCONCount <= partnerCONCount*2 {
log.Fatal().Msgf(
"will not bootstrap configuration without Byzantine majority of consensus nodes: "+
"(partners=%d, internals=%d, min_internals=%d)",
partnerCONCount, internalCONCount, partnerCONCount*2+1)
}
// Checks constraints about the number of partner and internal nodes.
// * Internal nodes must comprise >2/3 of each collector cluster.
// * for all roles R:
// all node with role R must have the same weight
func checkConstraints(partnerNodes, internalNodes []model.NodeInfo) {
partners := model.ToIdentityList(partnerNodes)
internals := model.ToIdentityList(internalNodes)
all := append(partners, internals...)

ensureUniformNodeWeightsPerRole(all)

// check collection committee Byzantine threshold for each cluster
// for checking Byzantine constraints, the seed doesn't matter
Expand Down
18 changes: 18 additions & 0 deletions cmd/bootstrap/cmd/dkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
model "github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/dkg"
"github.com/onflow/flow-go/model/encodable"
"github.com/onflow/flow-go/state/protocol/inmem"
)

func runDKG(nodes []model.NodeInfo) dkg.DKGData {
Expand All @@ -27,6 +28,12 @@ func runDKG(nodes []model.NodeInfo) dkg.DKGData {
}
log.Info().Msgf("finished running DKG")

pubKeyShares := make([]encodable.RandomBeaconPubKey, 0, len(dkgData.PubKeyShares))
for _, pubKey := range dkgData.PubKeyShares {
pubKeyShares = append(pubKeyShares, encodable.RandomBeaconPubKey{PublicKey: pubKey})
}

privKeyShares := make([]encodable.RandomBeaconPrivKey, 0, len(dkgData.PrivKeyShares))
for i, privKey := range dkgData.PrivKeyShares {
nodeID := nodes[i].NodeID

Expand All @@ -37,8 +44,19 @@ func runDKG(nodes []model.NodeInfo) dkg.DKGData {
GroupIndex: i,
}

privKeyShares = append(privKeyShares, encKey)

writeJSON(fmt.Sprintf(model.PathRandomBeaconPriv, nodeID), privParticpant)
}

// write full DKG info that will be used to construct QC
writeJSON(model.PathRootDKGData, inmem.EncodableFullDKG{
GroupKey: encodable.RandomBeaconPubKey{
PublicKey: dkgData.PubGroupKey,
},
PubKeyShares: pubKeyShares,
PrivKeyShares: privKeyShares,
})

return dkgData
}
2 changes: 1 addition & 1 deletion cmd/bootstrap/cmd/final_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func addFinalListFlags() {

func finalList(cmd *cobra.Command, args []string) {
// read public partner node infos
log.Info().Msgf("reading parnter public node information: %s", flagPartnerNodeInfoDir)
log.Info().Msgf("reading partner public node information: %s", flagPartnerNodeInfoDir)
partnerNodes := assemblePartnerNodesWithoutStake()

// read internal private node infos
Expand Down
118 changes: 88 additions & 30 deletions cmd/bootstrap/cmd/finalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (
"fmt"
"path/filepath"
"strings"
"time"

"github.com/onflow/cadence"
"github.com/spf13/cobra"

"github.com/onflow/flow-go/cmd"
"github.com/onflow/flow-go/cmd/bootstrap/run"
"github.com/onflow/flow-go/cmd/bootstrap/utils"
hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/fvm"
model "github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/dkg"
Expand All @@ -31,11 +32,9 @@ var (
flagCollectionClusters uint
flagPartnerNodeInfoDir string
flagPartnerStakes string
flagFastKG bool
flagRootChain string
flagRootParent string
flagRootHeight uint64
flagRootTimestamp string
flagDKGDataPath string
flagRootBlock string
flagRootBlockVotesDir string
flagRootCommit string
flagServiceAccountPublicKeyJSON string
flagGenesisTokenSupply string
Expand Down Expand Up @@ -76,26 +75,26 @@ func addFinalizeCmdFlags() {
" in the JSON file: Role, Address, NodeID, NetworkPubKey, StakingPubKey)")
finalizeCmd.Flags().StringVar(&flagPartnerStakes, "partner-stakes", "", "path to a JSON file containing "+
"a map from partner node's NodeID to their stake")
finalizeCmd.Flags().StringVar(&flagDKGDataPath, "dkg-data", "", "path to a JSON file containing data as output from DKG process")

cmd.MarkFlagRequired(finalizeCmd, "config")
cmd.MarkFlagRequired(finalizeCmd, "internal-priv-dir")
cmd.MarkFlagRequired(finalizeCmd, "partner-dir")
cmd.MarkFlagRequired(finalizeCmd, "partner-stakes")
cmd.MarkFlagRequired(finalizeCmd, "dkg-data")

// required parameters for generation of root block, root execution result and root block seal
finalizeCmd.Flags().StringVar(&flagRootChain, "root-chain", "local", "chain ID for the root block (can be 'main', 'test', 'canary', 'bench', or 'local'")
finalizeCmd.Flags().StringVar(&flagRootParent, "root-parent", "0000000000000000000000000000000000000000000000000000000000000000", "ID for the parent of the root block")
finalizeCmd.Flags().Uint64Var(&flagRootHeight, "root-height", 0, "height of the root block")
finalizeCmd.Flags().StringVar(&flagRootTimestamp, "root-timestamp", time.Now().UTC().Format(time.RFC3339), "timestamp of the root block (RFC3339)")
finalizeCmd.Flags().StringVar(&flagRootBlock, "root-block", "",
jordanschalm marked this conversation as resolved.
Show resolved Hide resolved
"path to a JSON file containing root block and votes")
durkmurder marked this conversation as resolved.
Show resolved Hide resolved
finalizeCmd.Flags().StringVar(&flagRootBlockVotesDir, "root-block-votes-dir", "", "path to directory with votes for root block")
finalizeCmd.Flags().StringVar(&flagRootCommit, "root-commit", "0000000000000000000000000000000000000000000000000000000000000000", "state commitment of root execution state")
finalizeCmd.Flags().Uint64Var(&flagEpochCounter, "epoch-counter", 0, "epoch counter for the epoch beginning with the root block")
finalizeCmd.Flags().Uint64Var(&flagNumViewsInEpoch, "epoch-length", 4000, "length of each epoch measured in views")
finalizeCmd.Flags().Uint64Var(&flagNumViewsInStakingAuction, "epoch-staking-phase-length", 100, "length of the epoch staking phase measured in views")
finalizeCmd.Flags().Uint64Var(&flagNumViewsInDKGPhase, "epoch-dkg-phase-length", 1000, "length of each DKG phase measured in views")

cmd.MarkFlagRequired(finalizeCmd, "root-chain")
cmd.MarkFlagRequired(finalizeCmd, "root-parent")
cmd.MarkFlagRequired(finalizeCmd, "root-height")
cmd.MarkFlagRequired(finalizeCmd, "root-block")
cmd.MarkFlagRequired(finalizeCmd, "root-block-votes-dir")
cmd.MarkFlagRequired(finalizeCmd, "root-commit")
cmd.MarkFlagRequired(finalizeCmd, "epoch-counter")
cmd.MarkFlagRequired(finalizeCmd, "epoch-length")
Expand All @@ -106,7 +105,6 @@ func addFinalizeCmdFlags() {

// optional parameters to influence various aspects of identity generation
finalizeCmd.Flags().UintVar(&flagCollectionClusters, "collection-clusters", 2, "number of collection clusters")
finalizeCmd.Flags().BoolVar(&flagFastKG, "fast-kg", false, "use fast (centralized) random beacon key generation instead of DKG")

// these two flags are only used when setup a network from genesis
finalizeCmd.Flags().StringVar(&flagServiceAccountPublicKeyJSON, "service-account-public-key-json",
Expand All @@ -128,11 +126,11 @@ func finalize(cmd *cobra.Command, args []string) {
log.Info().Msg("")

log.Info().Msg("collecting partner network and staking keys")
partnerNodes := assemblePartnerNodes()
partnerNodes := readPartnerNodeInfos()
log.Info().Msg("")

log.Info().Msg("generating internal private networking and staking keys")
internalNodes := assembleInternalNodes()
internalNodes := readInternalNodeInfos()
log.Info().Msg("")

log.Info().Msg("checking constraints on consensus/cluster nodes")
Expand All @@ -141,23 +139,27 @@ func finalize(cmd *cobra.Command, args []string) {

log.Info().Msg("assembling network and staking keys")
stakingNodes := mergeNodeInfos(internalNodes, partnerNodes)
writeJSON(model.PathNodeInfosPub, model.ToPublicNodeInfoList(stakingNodes))
log.Info().Msg("")

// create flow.IdentityList representation of participant set
participants := model.ToIdentityList(stakingNodes).Sort(order.Canonical)

log.Info().Msg("running DKG for consensus nodes")
dkgData := runDKG(model.FilterByRole(stakingNodes, flow.RoleConsensus))
log.Info().Msg("reading root block data")
block := readRootBlock()
log.Info().Msg("")

log.Info().Msg("constructing root block")
block := constructRootBlock(flagRootChain, flagRootParent, flagRootHeight, flagRootTimestamp)
log.Info().Msg("reading root block votes")
votes := readRootBlockVotes()
log.Info().Msg("")

log.Info().Msg("reading dkg data")
dkgData := readDKGData()
log.Info().Msg("")

log.Info().Msg("constructing root QC")
rootQC := constructRootQC(
block,
votes,
model.FilterByRole(stakingNodes, flow.RoleConsensus),
model.FilterByRole(internalNodes, flow.RoleConsensus),
dkgData,
Expand All @@ -180,6 +182,7 @@ func finalize(cmd *cobra.Command, args []string) {
// if no root commit is specified, bootstrap an empty execution state
if flagRootCommit == "0000000000000000000000000000000000000000000000000000000000000000" {
generateEmptyExecutionState(
block.Header.ChainID,
getRandomSource(flagBootstrapRandomSeed),
assignments,
clusterQCs,
Expand Down Expand Up @@ -252,9 +255,30 @@ func finalize(cmd *cobra.Command, args []string) {
log.Info().Msg("🌊 🏄 🤙 Done – ready to flow!")
}

// assemblePartnerNodes returns a list of partner nodes after gathering stake
// readRootBlockVotes reads votes for root block
func readRootBlockVotes() []*hotstuff.Vote {
var votes []*hotstuff.Vote
files, err := filesInDir(flagRootBlockVotesDir)
if err != nil {
log.Fatal().Err(err).Msg("could not read root block votes")
}
for _, f := range files {
// skip files that do not include node-infos
if !strings.Contains(f, model.FilenameRootBlockVotePrefix) {
continue
}

// read file and append to partners
var p hotstuff.Vote
readJSON(f, &p)
votes = append(votes, &p)
}
return votes
}

// readPartnerNodeInfos returns a list of partner nodes after gathering stake
// and public key information from configuration files
func assemblePartnerNodes() []model.NodeInfo {
func readPartnerNodeInfos() []model.NodeInfo {
partners := readPartnerNodes()
log.Info().Msgf("read %v partner node configuration files", len(partners))

Expand All @@ -279,8 +303,8 @@ func assemblePartnerNodes() []model.NodeInfo {
partner.Role,
partner.Address,
stake,
networkPubKey,
stakingPubKey,
networkPubKey.PublicKey,
stakingPubKey.PublicKey,
)
nodes = append(nodes, node)
}
Expand Down Expand Up @@ -309,9 +333,9 @@ func readPartnerNodes() []model.NodeInfoPub {
return partners
}

// assembleInternalNodes returns a list of internal nodes after collecting stakes
// readInternalNodeInfos returns a list of internal nodes after collecting stakes
// from configuration files
func assembleInternalNodes() []model.NodeInfo {
func readInternalNodeInfos() []model.NodeInfo {
privInternals := readInternalNodes()
log.Info().Msgf("read %v internal private node-info files", len(privInternals))

Expand Down Expand Up @@ -422,6 +446,39 @@ func mergeNodeInfos(internalNodes, partnerNodes []model.NodeInfo) []model.NodeIn
return nodes
}

// readRootBlock reads root block data from disc, this file needs to be prepared with
// rootblock command
func readRootBlock() *flow.Block {
rootBlock, err := utils.ReadRootBlock(flagRootBlock)
if err != nil {
log.Fatal().Err(err).Msg("could not read root block data")
}
return rootBlock
}

func readDKGData() dkg.DKGData {
encodableDKG, err := utils.ReadDKGData(flagDKGDataPath)
if err != nil {
log.Fatal().Err(err).Msg("could not read DKG data")
}

dkgData := dkg.DKGData{
PrivKeyShares: nil,
PubGroupKey: encodableDKG.GroupKey,
PubKeyShares: nil,
}

for _, pubKey := range encodableDKG.PubKeyShares {
dkgData.PubKeyShares = append(dkgData.PubKeyShares, pubKey.PublicKey)
}

for _, privKey := range encodableDKG.PrivKeyShares {
dkgData.PrivKeyShares = append(dkgData.PrivKeyShares, privKey.PrivateKey)
}

return dkgData
}

// Validation utility methods ------------------------------------------------

func validateNodeID(nodeID flow.Identifier) flow.Identifier {
Expand Down Expand Up @@ -468,10 +525,11 @@ func loadRootProtocolSnapshot(path string) (*inmem.Snapshot, error) {
// generateEmptyExecutionState generates a new empty execution state with the
// given configuration. Sets the flagRootCommit variable for future reads.
func generateEmptyExecutionState(
chainID flow.ChainID,
randomSource []byte,
assignments flow.AssignmentList,
clusterQCs []*flow.QuorumCertificate,
dkg dkg.DKGData,
dkgData dkg.DKGData,
identities flow.IdentityList,
) (commit flow.StateCommitment) {

Expand Down Expand Up @@ -504,13 +562,13 @@ func generateEmptyExecutionState(
RandomSource: cdcRandomSource,
CollectorClusters: assignments,
ClusterQCs: clusterQCs,
DKGPubKeys: dkg.PubKeyShares,
DKGPubKeys: dkgData.PubKeyShares,
}

commit, err = run.GenerateExecutionState(
filepath.Join(flagOutdir, model.DirnameExecutionState),
serviceAccountPublicKey,
parseChainID(flagRootChain).Chain(),
chainID.Chain(),
fvm.WithInitialTokenSupply(cdcInitialTokenSupply),
fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
Expand Down