From 4306c4357cc2be1c26acbca41b38c762dcbbac31 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Sat, 9 Jul 2022 00:55:31 -0500 Subject: [PATCH 01/13] add staking key PoP to keyGen command --- cmd/bootstrap/cmd/key.go | 14 ++++++------- cmd/bootstrap/cmd/keys.go | 2 +- cmd/bootstrap/utils/key_generation.go | 24 +++++++++++++++++----- cmd/bootstrap/utils/key_generation_test.go | 10 ++++++++- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/cmd/bootstrap/cmd/key.go b/cmd/bootstrap/cmd/key.go index 1650af20c92..81f7f0b1c79 100644 --- a/cmd/bootstrap/cmd/key.go +++ b/cmd/bootstrap/cmd/key.go @@ -79,7 +79,7 @@ func keyCmdRun(_ *cobra.Command, _ []string) { validateAddressFormat(flagAddress) // generate staking and network keys - networkKey, stakingKey, secretsDBKey, err := generateKeys() + networkKey, stakingKey, _, secretsDBKey, err := generateKeys() if err != nil { log.Fatal().Err(err).Msg("could not generate staking or network keys") } @@ -119,30 +119,30 @@ func keyCmdRun(_ *cobra.Command, _ []string) { } } -func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, []byte, error) { +func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, crypto.Signature, []byte, error) { log.Debug().Msg("will generate networking key") networkKey, err := utils.GenerateNetworkingKey(flagNetworkSeed) if err != nil { - return nil, nil, nil, fmt.Errorf("could not generate networking key: %w", err) + return nil, nil, nil, nil, fmt.Errorf("could not generate networking key: %w", err) } log.Info().Msg("generated networking key") log.Debug().Msg("will generate staking key") - stakingKey, err := utils.GenerateStakingKey(flagStakingSeed) + stakingKey, stakingKeyPoP, err := utils.GenerateStakingKey(flagStakingSeed) if err != nil { - return nil, nil, nil, fmt.Errorf("could not generate staking key: %w", err) + return nil, nil, nil, nil, fmt.Errorf("could not generate staking key: %w", err) } log.Info().Msg("generated staking key") log.Debug().Msg("will generate db encryption key") secretsDBKey, err := utils.GenerateSecretsDBEncryptionKey() if err != nil { - return nil, nil, nil, fmt.Errorf("could not generate secrets db encryption key: %w", err) + return nil, nil, nil, nil, fmt.Errorf("could not generate secrets db encryption key: %w", err) } log.Info().Msg("generated db encryption key") - return networkKey, stakingKey, secretsDBKey, nil + return networkKey, stakingKey, stakingKeyPoP, secretsDBKey, nil } func generateMachineAccountKey() (crypto.PrivateKey, error) { diff --git a/cmd/bootstrap/cmd/keys.go b/cmd/bootstrap/cmd/keys.go index 6cf01f61b6b..769e9049e8f 100644 --- a/cmd/bootstrap/cmd/keys.go +++ b/cmd/bootstrap/cmd/keys.go @@ -34,7 +34,7 @@ func genNetworkAndStakingKeys() []model.NodeInfo { log.Info().Msgf("generated %v networking keys for nodes in config", nodes) log.Debug().Msgf("will generate %v staking keys for nodes in config", nodes) - stakingKeys, err := utils.GenerateStakingKeys(nodes, GenerateRandomSeeds(nodes, crypto.KeyGenSeedMinLenBLSBLS12381)) + stakingKeys, _, err := utils.GenerateStakingKeys(nodes, GenerateRandomSeeds(nodes, crypto.KeyGenSeedMinLenBLSBLS12381)) if err != nil { log.Fatal().Err(err).Msg("cannot generate staking keys") } diff --git a/cmd/bootstrap/utils/key_generation.go b/cmd/bootstrap/utils/key_generation.go index 8a8164ef862..4e354ea5687 100644 --- a/cmd/bootstrap/utils/key_generation.go +++ b/cmd/bootstrap/utils/key_generation.go @@ -118,16 +118,30 @@ func GenerateNetworkingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) return GenerateKeys(crypto.ECDSAP256, n, seeds) } -func GenerateStakingKey(seed []byte) (crypto.PrivateKey, error) { +func GenerateStakingKey(seed []byte) (crypto.PrivateKey, crypto.Signature, error) { keys, err := GenerateKeys(crypto.BLSBLS12381, 1, [][]byte{seed}) if err != nil { - return nil, err + return nil, nil, err } - return keys[0], nil + pop, err := crypto.BLSGeneratePOP(keys[0]) + if err != nil { + return nil, nil, err + } + return keys[0], pop, nil } -func GenerateStakingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) { - return GenerateKeys(crypto.BLSBLS12381, n, seeds) +func GenerateStakingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, []crypto.Signature, error) { + keys := make([]crypto.PrivateKey, 0, n) + pops := make([]crypto.Signature, 0, n) + for i := 0; i < n; i++ { + key, pop, err := GenerateStakingKey(seeds[i]) + if err != nil { + return nil, nil, err + } + pops = append(pops, pop) + keys = append(keys, key) + } + return keys, pops, nil } func GenerateKeys(algo crypto.SigningAlgorithm, n int, seeds [][]byte) ([]crypto.PrivateKey, error) { diff --git a/cmd/bootstrap/utils/key_generation_test.go b/cmd/bootstrap/utils/key_generation_test.go index ab37e79b591..97af6562e8d 100644 --- a/cmd/bootstrap/utils/key_generation_test.go +++ b/cmd/bootstrap/utils/key_generation_test.go @@ -43,9 +43,17 @@ func TestGenerateKeys(t *testing.T) { } func TestGenerateStakingKeys(t *testing.T) { - keys, err := GenerateStakingKeys(2, unittest.SeedFixtures(2, crypto.KeyGenSeedMinLenBLSBLS12381)) + keys, pops, err := GenerateStakingKeys(2, unittest.SeedFixtures(2, crypto.KeyGenSeedMinLenBLSBLS12381)) require.NoError(t, err) require.Len(t, keys, 2) + require.Len(t, pops, 2) + + for i, key := range keys { + valid, err := crypto.BLSVerifyPOP(key.PublicKey(), pops[i]) + require.NoError(t, err) + require.True(t, valid) + } + } func TestWriteMachineAccountFiles(t *testing.T) { From 3805a242607a175fc77b56ec2fa127bcf4815835 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 13 Jul 2022 14:43:37 -0500 Subject: [PATCH 02/13] include staking key PoP in node info public --- cmd/bootstrap/cmd/final_list.go | 9 +++++++-- cmd/bootstrap/cmd/finalize.go | 17 +++++++++++++++-- cmd/bootstrap/cmd/keys.go | 12 ++++++++---- cmd/bootstrap/cmd/util.go | 5 ----- cmd/bootstrap/run/cluster_qc_test.go | 4 +++- cmd/bootstrap/run/qc_test.go | 3 ++- cmd/bootstrap/utils/node_info.go | 10 +++++----- cmd/bootstrap/utils/unittest.go | 2 ++ consensus/integration/nodes_test.go | 2 +- integration/testnet/container.go | 2 +- integration/testnet/network.go | 4 ++-- model/bootstrap/node_info.go | 27 +++++++++++++++++++++++---- model/encodable/keys.go | 25 +++++++++++++++++++++++++ utils/unittest/fixtures.go | 14 +++++++++----- 14 files changed, 103 insertions(+), 33 deletions(-) diff --git a/cmd/bootstrap/cmd/final_list.go b/cmd/bootstrap/cmd/final_list.go index ac1b000876b..37e8619f495 100644 --- a/cmd/bootstrap/cmd/final_list.go +++ b/cmd/bootstrap/cmd/final_list.go @@ -239,14 +239,17 @@ func assembleInternalNodesWithoutWeight() []model.NodeInfo { // validate every single internal node nodeID := validateNodeID(internal.NodeID) - node := model.NewPrivateNodeInfo( + node, err := model.NewPrivateNodeInfo( nodeID, internal.Role, internal.Address, flow.DefaultInitialWeight, internal.NetworkPrivKey, - internal.StakingPrivKey, + internal.StakingPrivKey.PrivateKey, ) + if err != nil { + panic(err) + } nodes = append(nodes, node) } @@ -275,6 +278,7 @@ func createPublicNodeInfo(nodes []model.NodeInfoPub) []model.NodeInfo { nodeID := validateNodeID(n.NodeID) networkPubKey := validateNetworkPubKey(n.NetworkPubKey) stakingPubKey := validateStakingPubKey(n.StakingPubKey) + stakingKeyPoP := validateStakingKeyPoP(n.StakingPoP) // all nodes should have equal weight node := model.NewPublicNodeInfo( @@ -284,6 +288,7 @@ func createPublicNodeInfo(nodes []model.NodeInfoPub) []model.NodeInfo { flow.DefaultInitialWeight, networkPubKey, stakingPubKey, + stakingKeyPoP, ) publicInfoNodes = append(publicInfoNodes, node) diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index 49f809e9f73..369e2609964 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -15,6 +15,7 @@ import ( "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/crypto" "github.com/onflow/flow-go/fvm" model "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/dkg" @@ -337,6 +338,7 @@ func readPartnerNodeInfos() []model.NodeInfo { nodeID := validateNodeID(partner.NodeID) networkPubKey := validateNetworkPubKey(partner.NetworkPubKey) stakingPubKey := validateStakingPubKey(partner.StakingPubKey) + stakingPoP := validateStakingKeyPoP(partner.StakingPoP) weight, valid := validateWeight(weights[partner.NodeID]) if !valid { log.Error().Msgf("weights: %v", weights) @@ -353,6 +355,7 @@ func readPartnerNodeInfos() []model.NodeInfo { weight, networkPubKey.PublicKey, stakingPubKey.PublicKey, + stakingPoP, ) nodes = append(nodes, node) } @@ -406,14 +409,17 @@ func readInternalNodeInfos() []model.NodeInfo { log.Warn().Msgf("internal node (id=%x) has non-default weight (%d != %d)", internal.NodeID, weight, flow.DefaultInitialWeight) } - node := model.NewPrivateNodeInfo( + node, err := model.NewPrivateNodeInfo( nodeID, internal.Role, internal.Address, weight, internal.NetworkPrivKey, - internal.StakingPrivKey, + internal.StakingPrivKey.PrivateKey, ) + if err != nil { + log.Fatal().Err(err).Msg("creating the node info failed") + } nodes = append(nodes, node) } @@ -553,6 +559,13 @@ func validateStakingPubKey(key encodable.StakingPubKey) encodable.StakingPubKey return key } +func validateStakingKeyPoP(pop encodable.StakingPoP) crypto.Signature { + if pop.Signature == nil { + log.Fatal().Msg("staking key proof of possession must not be nil") + } + return pop.Signature +} + func validateWeight(weight uint64) (uint64, bool) { return weight, weight > 0 } diff --git a/cmd/bootstrap/cmd/keys.go b/cmd/bootstrap/cmd/keys.go index 769e9049e8f..9854bd4ba77 100644 --- a/cmd/bootstrap/cmd/keys.go +++ b/cmd/bootstrap/cmd/keys.go @@ -50,7 +50,8 @@ func genNetworkAndStakingKeys() []model.NodeInfo { return model.Sort(internalNodes, order.Canonical) } -func assembleNodeInfo(nodeConfig model.NodeConfig, networkKey, stakingKey crypto.PrivateKey) model.NodeInfo { +func assembleNodeInfo(nodeConfig model.NodeConfig, networkKey, stakingKey crypto.PrivateKey, +) model.NodeInfo { var err error nodeID, found := getNameID() if !found { @@ -61,11 +62,11 @@ func assembleNodeInfo(nodeConfig model.NodeConfig, networkKey, stakingKey crypto } log.Debug(). - Str("networkPubKey", pubKeyToString(networkKey.PublicKey())). - Str("stakingPubKey", pubKeyToString(stakingKey.PublicKey())). + Str("networkPubKey", networkKey.PublicKey().String()). + Str("stakingPubKey", stakingKey.PublicKey().String()). Msg("encoded public staking and network keys") - nodeInfo := model.NewPrivateNodeInfo( + nodeInfo, err := model.NewPrivateNodeInfo( nodeID, nodeConfig.Role, nodeConfig.Address, @@ -73,6 +74,9 @@ func assembleNodeInfo(nodeConfig model.NodeConfig, networkKey, stakingKey crypto networkKey, stakingKey, ) + if err != nil { + log.Fatal().Err(err).Msg("creating node info failed") + } return nodeInfo } diff --git a/cmd/bootstrap/cmd/util.go b/cmd/bootstrap/cmd/util.go index d463b33f6ab..8ac81499f39 100644 --- a/cmd/bootstrap/cmd/util.go +++ b/cmd/bootstrap/cmd/util.go @@ -8,7 +8,6 @@ import ( "os" "path/filepath" - "github.com/onflow/flow-go/crypto" model "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/io" @@ -66,10 +65,6 @@ func writeText(path string, data []byte) { log.Info().Msgf("wrote file %v", path) } -func pubKeyToString(key crypto.PublicKey) string { - return fmt.Sprintf("%x", key.Encode()) -} - func filesInDir(dir string) ([]string, error) { exists, err := pathExists(dir) if err != nil { diff --git a/cmd/bootstrap/run/cluster_qc_test.go b/cmd/bootstrap/run/cluster_qc_test.go index 19a379d5b47..a4076d1c616 100644 --- a/cmd/bootstrap/run/cluster_qc_test.go +++ b/cmd/bootstrap/run/cluster_qc_test.go @@ -44,7 +44,8 @@ func createClusterParticipants(t *testing.T, n int) []model.NodeInfo { participants := make([]model.NodeInfo, n) for i, id := range ids { - participants[i] = model.NewPrivateNodeInfo( + var err error + participants[i], err = model.NewPrivateNodeInfo( id.NodeID, id.Role, id.Address, @@ -52,6 +53,7 @@ func createClusterParticipants(t *testing.T, n int) []model.NodeInfo { networkKeys[i], stakingKeys[i], ) + require.NoError(t, err) } return participants diff --git a/cmd/bootstrap/run/qc_test.go b/cmd/bootstrap/run/qc_test.go index afc8849d329..16fe42f2c48 100644 --- a/cmd/bootstrap/run/qc_test.go +++ b/cmd/bootstrap/run/qc_test.go @@ -58,7 +58,7 @@ func createSignerData(t *testing.T, n int) *ParticipantData { participantLookup[identity.NodeID] = lookupParticipant // add to participant list - nodeInfo := bootstrap.NewPrivateNodeInfo( + nodeInfo, err := bootstrap.NewPrivateNodeInfo( identity.NodeID, identity.Role, identity.Address, @@ -66,6 +66,7 @@ func createSignerData(t *testing.T, n int) *ParticipantData { networkingKeys[i], stakingKeys[i], ) + require.NoError(t, err) participants[i] = Participant{ NodeInfo: nodeInfo, RandomBeaconPrivKey: randomBSKs[i], diff --git a/cmd/bootstrap/utils/node_info.go b/cmd/bootstrap/utils/node_info.go index 711f77dabbc..09365252818 100644 --- a/cmd/bootstrap/utils/node_info.go +++ b/cmd/bootstrap/utils/node_info.go @@ -105,35 +105,35 @@ func GenerateNodeInfos(consensus, collection, execution, verification, access in nodes := make([]model.NodeInfo, 0) - // CONSENSUS = 1 + // CONSENSUS consensusNodes := unittest.NodeInfosFixture(consensus, unittest.WithRole(flow.RoleConsensus), unittest.WithWeight(1000), ) nodes = append(nodes, consensusNodes...) - // COLLECTION = 1 + // COLLECTION collectionNodes := unittest.NodeInfosFixture(collection, unittest.WithRole(flow.RoleCollection), unittest.WithWeight(1000), ) nodes = append(nodes, collectionNodes...) - // EXECUTION = 1 + // EXECUTION executionNodes := unittest.NodeInfosFixture(execution, unittest.WithRole(flow.RoleExecution), unittest.WithWeight(1000), ) nodes = append(nodes, executionNodes...) - // VERIFICATION = 1 + // VERIFICATION verificationNodes := unittest.NodeInfosFixture(verification, unittest.WithRole(flow.RoleVerification), unittest.WithWeight(1000), ) nodes = append(nodes, verificationNodes...) - // ACCESS = 1 + // ACCESS accessNodes := unittest.NodeInfosFixture(access, unittest.WithRole(flow.RoleAccess), unittest.WithWeight(1000), diff --git a/cmd/bootstrap/utils/unittest.go b/cmd/bootstrap/utils/unittest.go index e334441a1f5..a4eaadb2e4e 100644 --- a/cmd/bootstrap/utils/unittest.go +++ b/cmd/bootstrap/utils/unittest.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "os" "testing" @@ -11,6 +12,7 @@ import ( func RunWithSporkBootstrapDir(t testing.TB, f func(bootDir, partnerDir, partnerWeights, internalPrivDir, configPath string)) { dir := unittest.TempDir(t) + fmt.Println(dir) defer os.RemoveAll(dir) // make sure constraints are satisfied, 2/3's of con and col nodes are internal diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index a2ca6f42a37..54eaf61bf5f 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -282,7 +282,7 @@ func createPrivateNodeIdentities(n int) []bootstrap.NodeInfo { node.Address, node.Weight, networkPrivKey, - stakingPrivKey, + stakingPrivKey.PrivateKey, ) infos = append(infos, nodeInfo) } diff --git a/integration/testnet/container.go b/integration/testnet/container.go index 049ab874398..c635387e3b9 100644 --- a/integration/testnet/container.go +++ b/integration/testnet/container.go @@ -99,7 +99,7 @@ func NewContainerConfig(nodeName string, conf NodeConfig, networkKey, stakingKey GetPrivateNodeInfoAddress(nodeName), conf.Weight, networkKey, - stakingKey, + stakingKey.PrivateKey, ) containerConf := ContainerConfig{ diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 523e3f7ee62..92e8e8dbf28 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -1041,7 +1041,7 @@ func followerNodeInfos(confs []ConsensusFollowerConfig) ([]bootstrap.NodeInfo, e "", // no address 0, // no weight conf.NetworkingPrivKey, - dummyStakingKey, + dummyStakingKey.PrivateKey, ) nodeInfos = append(nodeInfos, info) @@ -1302,7 +1302,7 @@ func setupKeys(networkConf NetworkConfig) ([]ContainerConfig, error) { addr, conf.Weight, networkKeys[i], - stakingKeys[i], + stakingKeys[i].PrivateKey, ) containerConf := ContainerConfig{ diff --git a/model/bootstrap/node_info.go b/model/bootstrap/node_info.go index cdc6f855c4a..caf95434514 100644 --- a/model/bootstrap/node_info.go +++ b/model/bootstrap/node_info.go @@ -158,6 +158,7 @@ type NodeInfoPub struct { Weight uint64 NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey + StakingPoP encodable.StakingPoP } // decodableNodeInfoPub provides backward-compatible decoding of old models @@ -169,6 +170,7 @@ type decodableNodeInfoPub struct { Weight uint64 NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey + StakingPoP encodable.StakingPoP // Stake previously was used in place of the Weight field. // Deprecated: supported in decoding for backward-compatibility Stake uint64 @@ -193,6 +195,7 @@ func (info *NodeInfoPub) UnmarshalJSON(b []byte) error { info.Weight = decodable.Weight info.NetworkPubKey = decodable.NetworkPubKey info.StakingPubKey = decodable.StakingPubKey + info.StakingPoP = decodable.StakingPoP return nil } @@ -231,6 +234,7 @@ type NodeInfo struct { networkPrivKey crypto.PrivateKey stakingPubKey crypto.PublicKey stakingPrivKey crypto.PrivateKey + stakingPoP crypto.Signature } func NewPublicNodeInfo( @@ -240,6 +244,7 @@ func NewPublicNodeInfo( weight uint64, networkKey crypto.PublicKey, stakingKey crypto.PublicKey, + stakingPoP crypto.Signature, ) NodeInfo { return NodeInfo{ NodeID: nodeID, @@ -248,6 +253,7 @@ func NewPublicNodeInfo( Weight: weight, networkPubKey: networkKey, stakingPubKey: stakingKey, + stakingPoP: stakingPoP, } } @@ -258,7 +264,12 @@ func NewPrivateNodeInfo( weight uint64, networkKey crypto.PrivateKey, stakingKey crypto.PrivateKey, -) NodeInfo { +) (NodeInfo, error) { + pop, err := crypto.BLSGeneratePOP(stakingKey) + if err != nil { + return NodeInfo{}, fmt.Errorf("failed to generate node info: %w", err) + } + return NodeInfo{ NodeID: nodeID, Role: role, @@ -268,7 +279,8 @@ func NewPrivateNodeInfo( stakingPrivKey: stakingKey, networkPubKey: networkKey.PublicKey(), stakingPubKey: stakingKey.PublicKey(), - } + stakingPoP: pop, + }, nil } // Type returns the type of the node info instance. @@ -330,6 +342,7 @@ func (node NodeInfo) Public() NodeInfoPub { Weight: node.Weight, NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()}, StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()}, + StakingPoP: encodable.StakingPoP{Signature: node.stakingPoP}, } } @@ -358,17 +371,23 @@ func (node NodeInfo) Identity() *flow.Identity { } // NodeInfoFromIdentity converts an identity to a public NodeInfo +// WARNING: the staking PoP is dummy and not related to the identity public key func NodeInfoFromIdentity(identity *flow.Identity) NodeInfo { + dummyStakingPoP := make([]byte, crypto.SignatureLenBLSBLS12381) return NewPublicNodeInfo( identity.NodeID, identity.Role, identity.Address, identity.Weight, identity.NetworkPubKey, - identity.StakingPubKey) + identity.StakingPubKey, + dummyStakingPoP, + ) } -func PrivateNodeInfoFromIdentity(identity *flow.Identity, networkKey, stakingKey crypto.PrivateKey) NodeInfo { +// PrivateNodeInfoFromIdentity builds a NodeInfo from a flow Identity. +// WARNING: Nothing enforces that the output NodeInfo's keys are corresponding to the input Identity. +func PrivateNodeInfoFromIdentity(identity *flow.Identity, networkKey, stakingKey crypto.PrivateKey) (NodeInfo, error) { return NewPrivateNodeInfo( identity.NodeID, identity.Role, diff --git a/model/encodable/keys.go b/model/encodable/keys.go index 0049d4c24eb..10569bbf1cc 100644 --- a/model/encodable/keys.go +++ b/model/encodable/keys.go @@ -128,6 +128,31 @@ func (pub *StakingPubKey) UnmarshalJSON(b []byte) error { return err } +// StakingPoP wraps a PoP and allows it to be JSON encoded and decoded. +type StakingPoP struct { + crypto.Signature +} + +func (pop StakingPoP) MarshalJSON() ([]byte, error) { + if pop.Signature == nil { + return json.Marshal(nil) + } + return json.Marshal(toHex(pop.Signature)) +} + +func (pop *StakingPoP) UnmarshalJSON(b []byte) error { + bz, err := fromJSONHex(b) + if err != nil { + return err + } + + if len(bz) == 0 { + return nil + } + pop.Signature = bz + return err +} + // StakingPrivKey wraps a private key and allows it to be JSON encoded and decoded. It is not defined in the // crypto package since the crypto package should not know about the different key types. More importantly, private // keys should not be automatically encodable/serializable to prevent accidental secret sharing. The bootstrapping diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index b254c1563e9..ecf8ce89857 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -959,8 +959,8 @@ func NodeConfigFixture(opts ...func(*flow.Identity)) bootstrap.NodeConfig { } func NodeInfoFixture(opts ...func(*flow.Identity)) bootstrap.NodeInfo { - opts = append(opts, WithKeys) - return bootstrap.NodeInfoFromIdentity(IdentityFixture(opts...)) + nodes := NodeInfosFixture(1, opts...) + return nodes[0] } func NodeInfosFixture(n int, opts ...func(*flow.Identity)) []bootstrap.NodeInfo { @@ -977,7 +977,10 @@ func PrivateNodeInfosFixture(n int, opts ...func(*flow.Identity)) []bootstrap.No il := IdentityListFixture(n, opts...) nodeInfos := make([]bootstrap.NodeInfo, 0, n) for _, identity := range il { - nodeInfo := bootstrap.PrivateNodeInfoFromIdentity(identity, KeyFixture(crypto.ECDSAP256), KeyFixture(crypto.BLSBLS12381)) + nodeInfo, err := bootstrap.PrivateNodeInfoFromIdentity(identity, KeyFixture(crypto.ECDSAP256), KeyFixture(crypto.BLSBLS12381)) + if err != nil { + panic(err.Error()) + } nodeInfos = append(nodeInfos, nodeInfo) } return nodeInfos @@ -2018,8 +2021,9 @@ func PrivateKeyFixture(algo crypto.SigningAlgorithm, seedLength int) crypto.Priv // PrivateKeyFixtureByIdentifier returns a private key for a given node. // given the same identifier, it will always return the same private key func PrivateKeyFixtureByIdentifier(algo crypto.SigningAlgorithm, seedLength int, id flow.Identifier) crypto.PrivateKey { - seed := append(id[:], id[:]...) - sk, err := crypto.GeneratePrivateKey(algo, seed[:seedLength]) + seed := make([]byte, seedLength) + copy(seed, id[:]) + sk, err := crypto.GeneratePrivateKey(algo, seed) if err != nil { panic(err) } From 9973216277e02d1ab440546d67c77bd008ff68dd Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 13 Jul 2022 14:49:02 -0500 Subject: [PATCH 03/13] revoke PoP as a returned value from Key generation --- cmd/bootstrap/cmd/key.go | 14 +++++++------- cmd/bootstrap/cmd/keys.go | 2 +- cmd/bootstrap/utils/key_generation.go | 20 +++++++------------- cmd/bootstrap/utils/key_generation_test.go | 10 +--------- 4 files changed, 16 insertions(+), 30 deletions(-) diff --git a/cmd/bootstrap/cmd/key.go b/cmd/bootstrap/cmd/key.go index 81f7f0b1c79..1650af20c92 100644 --- a/cmd/bootstrap/cmd/key.go +++ b/cmd/bootstrap/cmd/key.go @@ -79,7 +79,7 @@ func keyCmdRun(_ *cobra.Command, _ []string) { validateAddressFormat(flagAddress) // generate staking and network keys - networkKey, stakingKey, _, secretsDBKey, err := generateKeys() + networkKey, stakingKey, secretsDBKey, err := generateKeys() if err != nil { log.Fatal().Err(err).Msg("could not generate staking or network keys") } @@ -119,30 +119,30 @@ func keyCmdRun(_ *cobra.Command, _ []string) { } } -func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, crypto.Signature, []byte, error) { +func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, []byte, error) { log.Debug().Msg("will generate networking key") networkKey, err := utils.GenerateNetworkingKey(flagNetworkSeed) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("could not generate networking key: %w", err) + return nil, nil, nil, fmt.Errorf("could not generate networking key: %w", err) } log.Info().Msg("generated networking key") log.Debug().Msg("will generate staking key") - stakingKey, stakingKeyPoP, err := utils.GenerateStakingKey(flagStakingSeed) + stakingKey, err := utils.GenerateStakingKey(flagStakingSeed) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("could not generate staking key: %w", err) + return nil, nil, nil, fmt.Errorf("could not generate staking key: %w", err) } log.Info().Msg("generated staking key") log.Debug().Msg("will generate db encryption key") secretsDBKey, err := utils.GenerateSecretsDBEncryptionKey() if err != nil { - return nil, nil, nil, nil, fmt.Errorf("could not generate secrets db encryption key: %w", err) + return nil, nil, nil, fmt.Errorf("could not generate secrets db encryption key: %w", err) } log.Info().Msg("generated db encryption key") - return networkKey, stakingKey, stakingKeyPoP, secretsDBKey, nil + return networkKey, stakingKey, secretsDBKey, nil } func generateMachineAccountKey() (crypto.PrivateKey, error) { diff --git a/cmd/bootstrap/cmd/keys.go b/cmd/bootstrap/cmd/keys.go index 9854bd4ba77..8e2cbb0c2a5 100644 --- a/cmd/bootstrap/cmd/keys.go +++ b/cmd/bootstrap/cmd/keys.go @@ -34,7 +34,7 @@ func genNetworkAndStakingKeys() []model.NodeInfo { log.Info().Msgf("generated %v networking keys for nodes in config", nodes) log.Debug().Msgf("will generate %v staking keys for nodes in config", nodes) - stakingKeys, _, err := utils.GenerateStakingKeys(nodes, GenerateRandomSeeds(nodes, crypto.KeyGenSeedMinLenBLSBLS12381)) + stakingKeys, err := utils.GenerateStakingKeys(nodes, GenerateRandomSeeds(nodes, crypto.KeyGenSeedMinLenBLSBLS12381)) if err != nil { log.Fatal().Err(err).Msg("cannot generate staking keys") } diff --git a/cmd/bootstrap/utils/key_generation.go b/cmd/bootstrap/utils/key_generation.go index 4e354ea5687..a2938f714b6 100644 --- a/cmd/bootstrap/utils/key_generation.go +++ b/cmd/bootstrap/utils/key_generation.go @@ -118,30 +118,24 @@ func GenerateNetworkingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) return GenerateKeys(crypto.ECDSAP256, n, seeds) } -func GenerateStakingKey(seed []byte) (crypto.PrivateKey, crypto.Signature, error) { +func GenerateStakingKey(seed []byte) (crypto.PrivateKey, error) { keys, err := GenerateKeys(crypto.BLSBLS12381, 1, [][]byte{seed}) if err != nil { - return nil, nil, err - } - pop, err := crypto.BLSGeneratePOP(keys[0]) - if err != nil { - return nil, nil, err + return nil, err } - return keys[0], pop, nil + return keys[0], nil } -func GenerateStakingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, []crypto.Signature, error) { +func GenerateStakingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) { keys := make([]crypto.PrivateKey, 0, n) - pops := make([]crypto.Signature, 0, n) for i := 0; i < n; i++ { - key, pop, err := GenerateStakingKey(seeds[i]) + key, err := GenerateStakingKey(seeds[i]) if err != nil { - return nil, nil, err + return nil, err } - pops = append(pops, pop) keys = append(keys, key) } - return keys, pops, nil + return keys, nil } func GenerateKeys(algo crypto.SigningAlgorithm, n int, seeds [][]byte) ([]crypto.PrivateKey, error) { diff --git a/cmd/bootstrap/utils/key_generation_test.go b/cmd/bootstrap/utils/key_generation_test.go index 97af6562e8d..ab37e79b591 100644 --- a/cmd/bootstrap/utils/key_generation_test.go +++ b/cmd/bootstrap/utils/key_generation_test.go @@ -43,17 +43,9 @@ func TestGenerateKeys(t *testing.T) { } func TestGenerateStakingKeys(t *testing.T) { - keys, pops, err := GenerateStakingKeys(2, unittest.SeedFixtures(2, crypto.KeyGenSeedMinLenBLSBLS12381)) + keys, err := GenerateStakingKeys(2, unittest.SeedFixtures(2, crypto.KeyGenSeedMinLenBLSBLS12381)) require.NoError(t, err) require.Len(t, keys, 2) - require.Len(t, pops, 2) - - for i, key := range keys { - valid, err := crypto.BLSVerifyPOP(key.PublicKey(), pops[i]) - require.NoError(t, err) - require.True(t, valid) - } - } func TestWriteMachineAccountFiles(t *testing.T) { From dbeaa4974e1b86bc5f51315aec46509ecd5f0393 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 13 Jul 2022 17:37:16 -0500 Subject: [PATCH 04/13] update the pub key validation to include PoP validation --- cmd/bootstrap/cmd/final_list.go | 3 +-- cmd/bootstrap/cmd/finalize.go | 20 ++++++++++++-------- cmd/bootstrap/utils/unittest.go | 2 -- model/bootstrap/node_info.go | 15 --------------- utils/unittest/fixtures.go | 22 +++++++++++++++++++++- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/cmd/bootstrap/cmd/final_list.go b/cmd/bootstrap/cmd/final_list.go index 37e8619f495..b7487fe6cc9 100644 --- a/cmd/bootstrap/cmd/final_list.go +++ b/cmd/bootstrap/cmd/final_list.go @@ -277,8 +277,7 @@ func createPublicNodeInfo(nodes []model.NodeInfoPub) []model.NodeInfo { // validate every single partner node nodeID := validateNodeID(n.NodeID) networkPubKey := validateNetworkPubKey(n.NetworkPubKey) - stakingPubKey := validateStakingPubKey(n.StakingPubKey) - stakingKeyPoP := validateStakingKeyPoP(n.StakingPoP) + stakingPubKey, stakingKeyPoP := validateStakingPubKey(n.StakingPubKey, n.StakingPoP) // all nodes should have equal weight node := model.NewPublicNodeInfo( diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index 369e2609964..aeb89938e5e 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -337,8 +337,7 @@ func readPartnerNodeInfos() []model.NodeInfo { // validate every single partner node nodeID := validateNodeID(partner.NodeID) networkPubKey := validateNetworkPubKey(partner.NetworkPubKey) - stakingPubKey := validateStakingPubKey(partner.StakingPubKey) - stakingPoP := validateStakingKeyPoP(partner.StakingPoP) + stakingPubKey, stakingPoP := validateStakingPubKey(partner.StakingPubKey, partner.StakingPoP) weight, valid := validateWeight(weights[partner.NodeID]) if !valid { log.Error().Msgf("weights: %v", weights) @@ -552,18 +551,23 @@ func validateNetworkPubKey(key encodable.NetworkPubKey) encodable.NetworkPubKey return key } -func validateStakingPubKey(key encodable.StakingPubKey) encodable.StakingPubKey { +func validateStakingPubKey(key encodable.StakingPubKey, pop encodable.StakingPoP) (encodable.StakingPubKey, crypto.Signature) { if key.PublicKey == nil { log.Fatal().Msg("StakingPubKey must not be nil") } - return key -} - -func validateStakingKeyPoP(pop encodable.StakingPoP) crypto.Signature { if pop.Signature == nil { log.Fatal().Msg("staking key proof of possession must not be nil") } - return pop.Signature + + valid, err := crypto.BLSVerifyPOP(key.PublicKey, pop.Signature) + if err != nil { + log.Fatal().Err(err).Msg("verifying staking key PoP failed") + } + if !valid { + log.Fatal().Err(err).Msg("saking key PoP is invalid") + } + + return key, pop.Signature } func validateWeight(weight uint64) (uint64, bool) { diff --git a/cmd/bootstrap/utils/unittest.go b/cmd/bootstrap/utils/unittest.go index a4eaadb2e4e..e334441a1f5 100644 --- a/cmd/bootstrap/utils/unittest.go +++ b/cmd/bootstrap/utils/unittest.go @@ -1,7 +1,6 @@ package utils import ( - "fmt" "os" "testing" @@ -12,7 +11,6 @@ import ( func RunWithSporkBootstrapDir(t testing.TB, f func(bootDir, partnerDir, partnerWeights, internalPrivDir, configPath string)) { dir := unittest.TempDir(t) - fmt.Println(dir) defer os.RemoveAll(dir) // make sure constraints are satisfied, 2/3's of con and col nodes are internal diff --git a/model/bootstrap/node_info.go b/model/bootstrap/node_info.go index caf95434514..6f3a522fdd3 100644 --- a/model/bootstrap/node_info.go +++ b/model/bootstrap/node_info.go @@ -370,21 +370,6 @@ func (node NodeInfo) Identity() *flow.Identity { return identity } -// NodeInfoFromIdentity converts an identity to a public NodeInfo -// WARNING: the staking PoP is dummy and not related to the identity public key -func NodeInfoFromIdentity(identity *flow.Identity) NodeInfo { - dummyStakingPoP := make([]byte, crypto.SignatureLenBLSBLS12381) - return NewPublicNodeInfo( - identity.NodeID, - identity.Role, - identity.Address, - identity.Weight, - identity.NetworkPubKey, - identity.StakingPubKey, - dummyStakingPoP, - ) -} - // PrivateNodeInfoFromIdentity builds a NodeInfo from a flow Identity. // WARNING: Nothing enforces that the output NodeInfo's keys are corresponding to the input Identity. func PrivateNodeInfoFromIdentity(identity *flow.Identity, networkKey, stakingKey crypto.PrivateKey) (NodeInfo, error) { diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index ecf8ce89857..4838763740b 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -963,12 +963,32 @@ func NodeInfoFixture(opts ...func(*flow.Identity)) bootstrap.NodeInfo { return nodes[0] } +// NodeInfoFromIdentity converts an identity to a public NodeInfo +// WARNING: the function replaces the staking key from the identity by a freshly generated one. +func NodeInfoFromIdentity(identity *flow.Identity) bootstrap.NodeInfo { + stakingSK := StakingPrivKeyFixture() + stakingPoP, err := crypto.BLSGeneratePOP(stakingSK) + if err != nil { + panic(err.Error()) + } + + return bootstrap.NewPublicNodeInfo( + identity.NodeID, + identity.Role, + identity.Address, + identity.Weight, + identity.NetworkPubKey, + stakingSK.PublicKey(), + stakingPoP, + ) +} + func NodeInfosFixture(n int, opts ...func(*flow.Identity)) []bootstrap.NodeInfo { opts = append(opts, WithKeys) il := IdentityListFixture(n, opts...) nodeInfos := make([]bootstrap.NodeInfo, 0, n) for _, identity := range il { - nodeInfos = append(nodeInfos, bootstrap.NodeInfoFromIdentity(identity)) + nodeInfos = append(nodeInfos, NodeInfoFromIdentity(identity)) } return nodeInfos } From 12fb45e7d54bfd07b43f91a090cf4d32bf844215 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Wed, 13 Jul 2022 19:33:21 -0500 Subject: [PATCH 05/13] add PoP to partner node info --- cmd/bootstrap/cmd/final_list.go | 45 ++++++++++++++++++++++------- cmd/bootstrap/cmd/key.go | 7 ++++- cmd/bootstrap/cmd/keygen.go | 14 +++++++-- cmd/bootstrap/cmd/rootblock.go | 6 +++- cmd/bootstrap/utils/node_info.go | 6 +++- model/bootstrap/node_info.go | 42 ++++++++++++++++++++++----- model/bootstrap/node_info_test.go | 3 +- model/bootstrap/partner_nodes.go.go | 1 + 8 files changed, 98 insertions(+), 26 deletions(-) diff --git a/cmd/bootstrap/cmd/final_list.go b/cmd/bootstrap/cmd/final_list.go index b7487fe6cc9..19e50fde466 100644 --- a/cmd/bootstrap/cmd/final_list.go +++ b/cmd/bootstrap/cmd/final_list.go @@ -1,6 +1,9 @@ package cmd import ( + "bytes" + "fmt" + "github.com/spf13/cobra" "github.com/onflow/flow-go/cmd" @@ -65,7 +68,11 @@ func finalList(cmd *cobra.Command, args []string) { validateNodes(localNodes, registeredNodes) // write node-config.json with the new list of nodes to be used for the `finalize` command - writeJSON(model.PathFinallist, model.ToPublicNodeInfoList(localNodes)) + pubInfo, err := model.ToPublicNodeInfoList(localNodes) + if err != nil { + log.Fatal().Err(err).Msg("failed to read public info") + } + writeJSON(model.PathFinallist, pubInfo) } func validateNodes(localNodes []model.NodeInfo, registeredNodes []model.NodeInfo) { @@ -125,28 +132,44 @@ func validateNodes(localNodes []model.NodeInfo, registeredNodes []model.NodeInfo // flow localNodes contain private key info if matchingNode.NetworkPubKey().String() != "" { // check networking pubkey match - matchNodeKey := matchingNode.NetworkPubKey().String() - registeredNodeKey := registeredNode.NetworkPubKey().String() + matchNodeKey := matchingNode.NetworkPubKey() + registeredNodeKey := registeredNode.NetworkPubKey() - if matchNodeKey != registeredNodeKey { + if !matchNodeKey.Equals(registeredNodeKey) { log.Error(). - Str("registered network key", registeredNodeKey). - Str("network key", matchNodeKey). + Str("registered network key", registeredNodeKey.String()). + Str("network key", matchNodeKey.String()). Msg("networking keys do not match") } } // flow localNodes contain privatekey info if matchingNode.StakingPubKey().String() != "" { - matchNodeKey := matchingNode.StakingPubKey().String() - registeredNodeKey := registeredNode.StakingPubKey().String() + matchNodeKey := matchingNode.StakingPubKey() + registeredNodeKey := registeredNode.StakingPubKey() - if matchNodeKey != registeredNodeKey { + if !matchNodeKey.Equals(registeredNodeKey) { log.Error(). - Str("registered staking key", registeredNodeKey). - Str("staking key", matchNodeKey). + Str("registered staking key", registeredNodeKey.String()). + Str("staking key", matchNodeKey.String()). Msg("staking keys do not match") } + + matchingPoP, err := matchingNode.StakingPoP() + if err != nil { + log.Error().Msgf("error reading matching PoP: %s", err.Error()) + } + registeredPoP, err := registeredNode.StakingPoP() + if err != nil { + log.Error().Msgf("error reading registered PoP: %s", err.Error()) + } + + if !bytes.Equal(matchingPoP, registeredPoP) { + log.Error(). + Str("registered staking PoP", fmt.Sprintf("%x", registeredPoP)). + Str("staking PoP", fmt.Sprintf("%x", matchingPoP)). + Msg("staking PoP do not match") + } } } } diff --git a/cmd/bootstrap/cmd/key.go b/cmd/bootstrap/cmd/key.go index 1650af20c92..788025e6a2f 100644 --- a/cmd/bootstrap/cmd/key.go +++ b/cmd/bootstrap/cmd/key.go @@ -97,11 +97,16 @@ func keyCmdRun(_ *cobra.Command, _ []string) { log.Fatal().Err(err).Msg("could not access private keys") } + public, err := nodeInfo.Public() + if err != nil { + log.Fatal().Err(err).Msg("could not access public keys") + } + // write files writeText(model.PathNodeID, []byte(nodeInfo.NodeID.String())) writeJSON(fmt.Sprintf(model.PathNodeInfoPriv, nodeInfo.NodeID), private) writeText(fmt.Sprintf(model.PathSecretsEncryptionKey, nodeInfo.NodeID), secretsDBKey) - writeJSON(fmt.Sprintf(model.PathNodeInfoPub, nodeInfo.NodeID), nodeInfo.Public()) + writeJSON(fmt.Sprintf(model.PathNodeInfoPub, nodeInfo.NodeID), public) // write machine account info if role == flow.RoleCollection || role == flow.RoleConsensus { diff --git a/cmd/bootstrap/cmd/keygen.go b/cmd/bootstrap/cmd/keygen.go index a13f21b1630..78cb3c0df51 100644 --- a/cmd/bootstrap/cmd/keygen.go +++ b/cmd/bootstrap/cmd/keygen.go @@ -91,7 +91,10 @@ var keygenCmd = &cobra.Command{ } log.Info().Msg("generating node public information") - genNodePubInfo(nodes) + err = genNodePubInfo(nodes) + if err != nil { + log.Fatal().Err(err).Msg("failed to generate nodes public info") + } }, } @@ -122,10 +125,15 @@ func isEmptyDir(path string) (bool, error) { return false, err // Either not empty or error, suits both cases } -func genNodePubInfo(nodes []model.NodeInfo) { +func genNodePubInfo(nodes []model.NodeInfo) error { pubNodes := make([]model.NodeInfoPub, 0, len(nodes)) for _, node := range nodes { - pubNodes = append(pubNodes, node.Public()) + pub, err := node.Public() + if err != nil { + return fmt.Errorf("failed to read public info: %w", err) + } + pubNodes = append(pubNodes, pub) } writeJSON(model.PathInternalNodeInfosPub, pubNodes) + return nil } diff --git a/cmd/bootstrap/cmd/rootblock.go b/cmd/bootstrap/cmd/rootblock.go index c61a95fac08..ab35cfaf48e 100644 --- a/cmd/bootstrap/cmd/rootblock.go +++ b/cmd/bootstrap/cmd/rootblock.go @@ -100,7 +100,11 @@ func rootBlock(cmd *cobra.Command, args []string) { log.Info().Msg("assembling network and staking keys") stakingNodes := mergeNodeInfos(internalNodes, partnerNodes) - writeJSON(model.PathNodeInfosPub, model.ToPublicNodeInfoList(stakingNodes)) + publicInfo, err := model.ToPublicNodeInfoList(stakingNodes) + if err != nil { + log.Fatal().Msg("failed to read public node info") + } + writeJSON(model.PathNodeInfosPub, publicInfo) log.Info().Msg("") log.Info().Msg("running DKG for consensus nodes") diff --git a/cmd/bootstrap/utils/node_info.go b/cmd/bootstrap/utils/node_info.go index 09365252818..b507e2d8c23 100644 --- a/cmd/bootstrap/utils/node_info.go +++ b/cmd/bootstrap/utils/node_info.go @@ -20,7 +20,11 @@ func WritePartnerFiles(nodeInfos []model.NodeInfo, bootDir string) (string, stri nodePubInfos := make([]model.NodeInfoPub, len(nodeInfos)) weights := make(map[flow.Identifier]uint64) for i, node := range nodeInfos { - nodePubInfos[i] = node.Public() + var err error + nodePubInfos[i], err = node.Public() + if err != nil { + return "", "", fmt.Errorf("could not read public info: %w", err) + } weights[node.NodeID] = node.Weight } diff --git a/model/bootstrap/node_info.go b/model/bootstrap/node_info.go index 6f3a522fdd3..cf829613c7b 100644 --- a/model/bootstrap/node_info.go +++ b/model/bootstrap/node_info.go @@ -308,6 +308,17 @@ func (node NodeInfo) StakingPubKey() crypto.PublicKey { return node.stakingPrivKey.PublicKey() } +func (node NodeInfo) StakingPoP() (crypto.Signature, error) { + if node.stakingPoP != nil { + return node.stakingPoP, nil + } + pop, err := crypto.BLSGeneratePOP(node.stakingPrivKey) + if err != nil { + return nil, fmt.Errorf("staking PoP generation failed: %w", err) + } + return pop, nil +} + func (node NodeInfo) PrivateKeys() (*NodePrivateKeys, error) { if node.Type() != NodeInfoTypePrivate { return nil, ErrMissingPrivateInfo @@ -334,7 +345,12 @@ func (node NodeInfo) Private() (NodeInfoPriv, error) { } // Public returns the canonical public encodable structure -func (node NodeInfo) Public() NodeInfoPub { +func (node NodeInfo) Public() (NodeInfoPub, error) { + stakingPoP, err := node.StakingPoP() + if err != nil { + return NodeInfoPub{}, fmt.Errorf("failed to generate staking PoP: %w", err) + } + return NodeInfoPub{ Role: node.Role, Address: node.Address, @@ -342,19 +358,25 @@ func (node NodeInfo) Public() NodeInfoPub { Weight: node.Weight, NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()}, StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()}, - StakingPoP: encodable.StakingPoP{Signature: node.stakingPoP}, - } + StakingPoP: encodable.StakingPoP{Signature: stakingPoP}, + }, nil } // PartnerPublic returns the public data for a partner node. -func (node NodeInfo) PartnerPublic() PartnerNodeInfoPub { +func (node NodeInfo) PartnerPublic() (PartnerNodeInfoPub, error) { + + stakingPoP, err := node.StakingPoP() + if err != nil { + return PartnerNodeInfoPub{}, fmt.Errorf("failed to generate staking PoP: %w", err) + } return PartnerNodeInfoPub{ Role: node.Role, Address: node.Address, NodeID: node.NodeID, NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()}, StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()}, - } + StakingPoP: encodable.StakingPoP{Signature: stakingPoP}, + }, nil } // Identity returns the node info as a public Flow identity. @@ -412,10 +434,14 @@ func ToIdentityList(nodes []NodeInfo) flow.IdentityList { return il } -func ToPublicNodeInfoList(nodes []NodeInfo) []NodeInfoPub { +func ToPublicNodeInfoList(nodes []NodeInfo) ([]NodeInfoPub, error) { pub := make([]NodeInfoPub, 0, len(nodes)) for _, node := range nodes { - pub = append(pub, node.Public()) + info, err := node.Public() + if err != nil { + return nil, fmt.Errorf("could not read public info: %w", err) + } + pub = append(pub, info) } - return pub + return pub, nil } diff --git a/model/bootstrap/node_info_test.go b/model/bootstrap/node_info_test.go index 536c0c808f9..205af147401 100644 --- a/model/bootstrap/node_info_test.go +++ b/model/bootstrap/node_info_test.go @@ -44,7 +44,8 @@ func TestNodeConfigEncodingJSON(t *testing.T) { func TestNodeInfoPubEncodingJSON(t *testing.T) { t.Run("normal node info", func(t *testing.T) { - conf := unittest.NodeInfoFixture().Public() + conf, err := unittest.NodeInfoFixture().Public() + require.NoError(t, err) enc, err := json.Marshal(conf) require.NoError(t, err) var dec bootstrap.NodeInfoPub diff --git a/model/bootstrap/partner_nodes.go.go b/model/bootstrap/partner_nodes.go.go index a65f09d2e18..23f3840422d 100644 --- a/model/bootstrap/partner_nodes.go.go +++ b/model/bootstrap/partner_nodes.go.go @@ -14,4 +14,5 @@ type PartnerNodeInfoPub struct { NodeID flow.Identifier NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey + StakingPoP encodable.StakingPoP } From eb78b87aeed3ae31493fc07fa4ddd20a8dcb411e Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 14 Jul 2022 03:39:49 -0500 Subject: [PATCH 06/13] use []byte JSON encoding without wrapper + add PoP field from Cadence script --- cmd/bootstrap/cmd/finalize.go | 10 +++++----- cmd/bootstrap/cmd/partner_infos.go | 7 +++++++ model/bootstrap/node_info.go | 8 ++++---- model/bootstrap/partner_nodes.go.go | 2 +- model/encodable/keys.go | 25 ------------------------- 5 files changed, 17 insertions(+), 35 deletions(-) diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index aeb89938e5e..26eda30698c 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -77,7 +77,7 @@ func addFinalizeCmdFlags() { "containing the output from the `keygen` command for internal nodes") finalizeCmd.Flags().StringVar(&flagPartnerNodeInfoDir, "partner-dir", "", "path to directory "+ "containing one JSON file starting with node-info.pub..json for every partner node (fields "+ - " in the JSON file: Role, Address, NodeID, NetworkPubKey, StakingPubKey)") + " in the JSON file: Role, Address, NodeID, NetworkPubKey, StakingPubKey, StakingKeyPoP)") // Deprecated: remove this flag finalizeCmd.Flags().StringVar(&deprecatedFlagPartnerStakes, "partner-stakes", "", "deprecated: use partner-weights instead") finalizeCmd.Flags().StringVar(&flagPartnerWeights, "partner-weights", "", "path to a JSON file containing "+ @@ -551,15 +551,15 @@ func validateNetworkPubKey(key encodable.NetworkPubKey) encodable.NetworkPubKey return key } -func validateStakingPubKey(key encodable.StakingPubKey, pop encodable.StakingPoP) (encodable.StakingPubKey, crypto.Signature) { +func validateStakingPubKey(key encodable.StakingPubKey, pop []byte) (encodable.StakingPubKey, []byte) { if key.PublicKey == nil { log.Fatal().Msg("StakingPubKey must not be nil") } - if pop.Signature == nil { + if pop == nil { log.Fatal().Msg("staking key proof of possession must not be nil") } - valid, err := crypto.BLSVerifyPOP(key.PublicKey, pop.Signature) + valid, err := crypto.BLSVerifyPOP(key.PublicKey, pop) if err != nil { log.Fatal().Err(err).Msg("verifying staking key PoP failed") } @@ -567,7 +567,7 @@ func validateStakingPubKey(key encodable.StakingPubKey, pop encodable.StakingPoP log.Fatal().Err(err).Msg("saking key PoP is invalid") } - return key, pop.Signature + return key, pop } func validateWeight(weight uint64) (uint64, bool) { diff --git a/cmd/bootstrap/cmd/partner_infos.go b/cmd/bootstrap/cmd/partner_infos.go index 4c9ded401c8..36e688ecc99 100644 --- a/cmd/bootstrap/cmd/partner_infos.go +++ b/cmd/bootstrap/cmd/partner_infos.go @@ -26,6 +26,7 @@ const ( networkingAddressField networkingKeyField stakingKeyField + stakingKePOPyField ) const ( @@ -170,6 +171,11 @@ func parseNodeInfo(info cadence.Value) (*bootstrap.NodeInfoPub, error) { return nil, fmt.Errorf("failed to decode staking public key: %w", err) } + stakingPOP, err := hex.DecodeString(string(fields[stakingKePOPyField].(cadence.String))) + if err != nil { + return nil, fmt.Errorf("failed to decode staking private key PoP hex (%s): %w", string(fields[stakingKePOPyField].(cadence.String)), err) + } + return &bootstrap.NodeInfoPub{ Role: flow.Role(fields[roleField].(cadence.UInt8)), Address: string(fields[networkingAddressField].(cadence.String)), @@ -177,6 +183,7 @@ func parseNodeInfo(info cadence.Value) (*bootstrap.NodeInfoPub, error) { Weight: flow.DefaultInitialWeight, NetworkPubKey: encodable.NetworkPubKey{PublicKey: networkPubKey}, StakingPubKey: encodable.StakingPubKey{PublicKey: stakingPubKey}, + StakingPoP: stakingPOP, }, nil } diff --git a/model/bootstrap/node_info.go b/model/bootstrap/node_info.go index cf829613c7b..79ebacfb3a3 100644 --- a/model/bootstrap/node_info.go +++ b/model/bootstrap/node_info.go @@ -158,7 +158,7 @@ type NodeInfoPub struct { Weight uint64 NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey - StakingPoP encodable.StakingPoP + StakingPoP []byte } // decodableNodeInfoPub provides backward-compatible decoding of old models @@ -170,7 +170,7 @@ type decodableNodeInfoPub struct { Weight uint64 NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey - StakingPoP encodable.StakingPoP + StakingPoP []byte // Stake previously was used in place of the Weight field. // Deprecated: supported in decoding for backward-compatibility Stake uint64 @@ -358,7 +358,7 @@ func (node NodeInfo) Public() (NodeInfoPub, error) { Weight: node.Weight, NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()}, StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()}, - StakingPoP: encodable.StakingPoP{Signature: stakingPoP}, + StakingPoP: stakingPoP, }, nil } @@ -375,7 +375,7 @@ func (node NodeInfo) PartnerPublic() (PartnerNodeInfoPub, error) { NodeID: node.NodeID, NetworkPubKey: encodable.NetworkPubKey{PublicKey: node.NetworkPubKey()}, StakingPubKey: encodable.StakingPubKey{PublicKey: node.StakingPubKey()}, - StakingPoP: encodable.StakingPoP{Signature: stakingPoP}, + StakingPoP: stakingPoP, }, nil } diff --git a/model/bootstrap/partner_nodes.go.go b/model/bootstrap/partner_nodes.go.go index 23f3840422d..36e5c9cc41a 100644 --- a/model/bootstrap/partner_nodes.go.go +++ b/model/bootstrap/partner_nodes.go.go @@ -14,5 +14,5 @@ type PartnerNodeInfoPub struct { NodeID flow.Identifier NetworkPubKey encodable.NetworkPubKey StakingPubKey encodable.StakingPubKey - StakingPoP encodable.StakingPoP + StakingPoP []byte } diff --git a/model/encodable/keys.go b/model/encodable/keys.go index 10569bbf1cc..0049d4c24eb 100644 --- a/model/encodable/keys.go +++ b/model/encodable/keys.go @@ -128,31 +128,6 @@ func (pub *StakingPubKey) UnmarshalJSON(b []byte) error { return err } -// StakingPoP wraps a PoP and allows it to be JSON encoded and decoded. -type StakingPoP struct { - crypto.Signature -} - -func (pop StakingPoP) MarshalJSON() ([]byte, error) { - if pop.Signature == nil { - return json.Marshal(nil) - } - return json.Marshal(toHex(pop.Signature)) -} - -func (pop *StakingPoP) UnmarshalJSON(b []byte) error { - bz, err := fromJSONHex(b) - if err != nil { - return err - } - - if len(bz) == 0 { - return nil - } - pop.Signature = bz - return err -} - // StakingPrivKey wraps a private key and allows it to be JSON encoded and decoded. It is not defined in the // crypto package since the crypto package should not know about the different key types. More importantly, private // keys should not be automatically encodable/serializable to prevent accidental secret sharing. The bootstrapping From 3dad8a11858a9af24ba50b19d30983ba18d08cc4 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 14 Jul 2022 14:17:59 -0500 Subject: [PATCH 07/13] fix test errors --- consensus/integration/epoch_test.go | 2 +- consensus/integration/nodes_test.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/consensus/integration/epoch_test.go b/consensus/integration/epoch_test.go index 6da5e3fe943..a843a9e9555 100644 --- a/consensus/integration/epoch_test.go +++ b/consensus/integration/epoch_test.go @@ -110,7 +110,7 @@ func TestStaticEpochTransition(t *testing.T) { func TestEpochTransition_IdentitiesOverlap(t *testing.T) { // must finalize 8 blocks, we specify the epoch transition after 4 views stopper := NewStopper(8, 0) - privateNodeInfos := createPrivateNodeIdentities(4) + privateNodeInfos := createPrivateNodeIdentities(t, 4) firstEpochConsensusParticipants := completeConsensusIdentities(t, privateNodeInfos[:3]) rootSnapshot := createRootSnapshot(t, firstEpochConsensusParticipants) consensusParticipants := NewConsensusParticipants(firstEpochConsensusParticipants) diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index 54eaf61bf5f..6f83cd2a020 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -270,20 +270,21 @@ func createRootBlockData(participantData *run.ParticipantData) (*flow.Block, *fl return root, result, seal } -func createPrivateNodeIdentities(n int) []bootstrap.NodeInfo { +func createPrivateNodeIdentities(t *testing.T, n int) []bootstrap.NodeInfo { consensus := unittest.IdentityListFixture(n, unittest.WithRole(flow.RoleConsensus)).Sort(order.Canonical) infos := make([]bootstrap.NodeInfo, 0, n) for _, node := range consensus { networkPrivKey := unittest.NetworkingPrivKeyFixture() stakingPrivKey := unittest.StakingPrivKeyFixture() - nodeInfo := bootstrap.NewPrivateNodeInfo( + nodeInfo, err := bootstrap.NewPrivateNodeInfo( node.NodeID, node.Role, node.Address, node.Weight, networkPrivKey, - stakingPrivKey.PrivateKey, + stakingPrivKey, ) + require.NoError(t, err) infos = append(infos, nodeInfo) } return infos @@ -291,7 +292,7 @@ func createPrivateNodeIdentities(n int) []bootstrap.NodeInfo { func createConsensusIdentities(t *testing.T, n int) *run.ParticipantData { // create n consensus node participants - consensus := createPrivateNodeIdentities(n) + consensus := createPrivateNodeIdentities(t, n) return completeConsensusIdentities(t, consensus) } From 0374238e167b485a676fd73bcced0381e97afa88 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 14 Jul 2022 14:39:07 -0500 Subject: [PATCH 08/13] update bootsrapping README --- cmd/bootstrap/README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/bootstrap/README.md b/cmd/bootstrap/README.md index 9000f4d87f4..643533d11c4 100644 --- a/cmd/bootstrap/README.md +++ b/cmd/bootstrap/README.md @@ -3,9 +3,9 @@ This package contains script for generating the bootstrap files needed to initialize the Flow network. The high-level bootstrapping process is described in [Notion](https://www.notion.so/dapperlabs/Flow-Bootstrapping-ce9d227f18a8410dbce74ed7d4ddee27). -WARNING: These scripts use Go's crypto/rand package to generate seeds for private keys. Make sure you are running the bootstrap scripts on a machine that does provide proper a low-level implementation. See https://golang.org/pkg/crypto/rand/ for details. +WARNING: These scripts use Go's crypto/rand package to generate seeds for private keys, whenever seeds are not provided to the commands. Make sure you are running the bootstrap scripts on a machine that does provide a low-level cryptographically secure RNG. See https://golang.org/pkg/crypto/rand/ for details. -NOTE: Public and private keys are encoded in JSON files as base64 strings, not as hex, contrary to what might be expected. +NOTE: Public and private keys are encoded in JSON files as hex strings. Code structure: * `cmd/bootstrap/cmd` contains CLI logic that can exit the program and read/write files. It also uses structures and data types that are purely relevant for CLI purposes, such as encoding, decoding, etc. @@ -18,9 +18,9 @@ Code structure: The bootstrapping will generate the following information: #### Per node -* Staking key (BLS key with curve BLS12-381) -* Networking key (ECDSA key) -* Random beacon key; _only_ for consensus nodes (BLS based on Joint-Feldman DKG for threshold signatures) +* Staking key (BLS key on curve BLS12-381) +* Networking key (ECDSA key on curve P-256) +* Random beacon key; _only_ for consensus nodes (BLS key on curve BLS12-381, used for a BLS-based threshold signatures) #### Node Identities * List of all authorized Flow nodes @@ -28,6 +28,7 @@ The bootstrapping will generate the following information: - node ID - node role - public staking key + - proof of possession of the staking private key - public networking key - weight @@ -61,7 +62,8 @@ Values directly specified as command line parameters: Values can be specified as command line parameters: - seed for generating staking key (min 48 bytes in hex encoding) - seed for generating networking key (min 48 bytes in hex encoding) -If seeds are not provided, the CLI will try to use the system's pseudo-random number generator (PRNG), e. g. `dev/urandom`. Make sure you are running the CLI on a hardware that has a cryptographically secure PRNG, or provide seeds generated on such a system. +Provided seeds must be of high entropy, ideally generated by a crypto secure RNG. +If seeds are not provided, the CLI will try to use the system's random number generator (RNG), e. g. `dev/urandom`. Make sure you are running the CLI on a hardware that has a cryptographically secure RNG. #### Example ```bash From fecabfd2751db86cada99c75f1fccd9f7110787a Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 14 Jul 2022 14:47:05 -0500 Subject: [PATCH 09/13] fix another test --- model/bootstrap/node_info_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/model/bootstrap/node_info_test.go b/model/bootstrap/node_info_test.go index 205af147401..bf0c35cd49c 100644 --- a/model/bootstrap/node_info_test.go +++ b/model/bootstrap/node_info_test.go @@ -54,7 +54,8 @@ func TestNodeInfoPubEncodingJSON(t *testing.T) { assert.Equal(t, conf, dec) }) t.Run("compat: should accept old files using Stake field", func(t *testing.T) { - conf := unittest.NodeInfoFixture().Public() + conf, err := unittest.NodeInfoFixture().Public() + require.NoError(t, err) enc, err := json.Marshal(conf) require.NoError(t, err) // emulate the old encoding by replacing the new field with old field name From f1b1284d954888229fa1a358aa9f69dec8021593 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Thu, 14 Jul 2022 15:14:08 -0500 Subject: [PATCH 10/13] update integration testnet tests --- integration/testnet/container.go | 12 ++++++++---- integration/testnet/network.go | 14 ++++++++++---- integration/tests/epochs/suite.go | 5 +++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/integration/testnet/container.go b/integration/testnet/container.go index c635387e3b9..f8b41da9cc7 100644 --- a/integration/testnet/container.go +++ b/integration/testnet/container.go @@ -92,15 +92,19 @@ func GetPrivateNodeInfoAddress(nodeName string) string { return fmt.Sprintf("%s:%d", nodeName, DefaultFlowPort) } -func NewContainerConfig(nodeName string, conf NodeConfig, networkKey, stakingKey crypto.PrivateKey) ContainerConfig { - info := bootstrap.NewPrivateNodeInfo( +func NewContainerConfig(nodeName string, conf NodeConfig, networkKey, stakingKey crypto.PrivateKey, +) (ContainerConfig, error) { + info, err := bootstrap.NewPrivateNodeInfo( conf.Identifier, conf.Role, GetPrivateNodeInfoAddress(nodeName), conf.Weight, networkKey, - stakingKey.PrivateKey, + stakingKey, ) + if err != nil { + return ContainerConfig{}, err + } containerConf := ContainerConfig{ NodeInfo: info, @@ -113,7 +117,7 @@ func NewContainerConfig(nodeName string, conf NodeConfig, networkKey, stakingKey Corrupted: conf.Corrupted, } - return containerConf + return containerConf, nil } // ImageName returns the Docker image name for the given config. diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 92e8e8dbf28..465a35f3a72 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -1035,14 +1035,17 @@ func followerNodeInfos(confs []ConsensusFollowerConfig) ([]bootstrap.NodeInfo, e dummyStakingKey := unittest.StakingPrivKeyFixture() for _, conf := range confs { - info := bootstrap.NewPrivateNodeInfo( + info, err := bootstrap.NewPrivateNodeInfo( conf.NodeID, flow.RoleAccess, // use Access role "", // no address 0, // no weight conf.NetworkingPrivKey, - dummyStakingKey.PrivateKey, + dummyStakingKey, ) + if err != nil { + return nil, err + } nodeInfos = append(nodeInfos, info) } @@ -1296,14 +1299,17 @@ func setupKeys(networkConf NetworkConfig) ([]ContainerConfig, error) { addr := fmt.Sprintf("%s:%d", name, DefaultFlowPort) roleCounter[conf.Role]++ - info := bootstrap.NewPrivateNodeInfo( + info, err := bootstrap.NewPrivateNodeInfo( conf.Identifier, conf.Role, addr, conf.Weight, networkKeys[i], - stakingKeys[i].PrivateKey, + stakingKeys[i], ) + if err != nil { + return nil, err + } containerConf := ContainerConfig{ NodeInfo: info, diff --git a/integration/tests/epochs/suite.go b/integration/tests/epochs/suite.go index dc0aaad727b..072a9860f36 100644 --- a/integration/tests/epochs/suite.go +++ b/integration/tests/epochs/suite.go @@ -565,8 +565,9 @@ func (s *Suite) newTestContainerOnNetwork(role flow.Role, info *StakedNodeOperat } nodeConfig := testnet.NewNodeConfig(role, containerConfigs...) - testContainerConfig := testnet.NewContainerConfig(info.ContainerName, nodeConfig, info.NetworkingKey, info.StakingKey) - err := testContainerConfig.WriteKeyFiles(s.net.BootstrapDir, info.MachineAccountAddress, encodable.MachineAccountPrivKey{PrivateKey: info.MachineAccountKey}, role) + testContainerConfig, err := testnet.NewContainerConfig(info.ContainerName, nodeConfig, info.NetworkingKey, info.StakingKey) + require.NoError(s.T(), err) + err = testContainerConfig.WriteKeyFiles(s.net.BootstrapDir, info.MachineAccountAddress, encodable.MachineAccountPrivKey{PrivateKey: info.MachineAccountKey}, role) require.NoError(s.T(), err) //add our container to the network From 36e9c9c8c6115ce7165c85a773e5314936573daa Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 10 May 2024 01:05:56 +0800 Subject: [PATCH 11/13] fix more errors --- integration/testnet/util.go | 6 +++++- integration/tests/epochs/dynamic_epoch_transition_suite.go | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/integration/testnet/util.go b/integration/testnet/util.go index c48d9bc0afd..6559a819dc5 100644 --- a/integration/testnet/util.go +++ b/integration/testnet/util.go @@ -171,7 +171,7 @@ func WriteTestExecutionService(_ flow.Identifier, address, observerName, bootstr log.Info().Msgf("test execution node private key: %v, public key: %x, peerID: %v, nodeID: %v", networkKey, k, peerID, nodeID) - nodeInfo := bootstrap.NewPrivateNodeInfo( + nodeInfo, err := bootstrap.NewPrivateNodeInfo( nodeID, flow.RoleExecution, address, @@ -180,6 +180,10 @@ func WriteTestExecutionService(_ flow.Identifier, address, observerName, bootstr stakingKey, ) + if err != nil { + return bootstrap.NodeInfo{}, fmt.Errorf("failed to create node info: %w", err) + } + path := fmt.Sprintf("%s/private-root-information/private-node-info_%v/%vjson", bootstrapDir, nodeID, bootstrap.PathPrivNodeInfoPrefix) diff --git a/integration/tests/epochs/dynamic_epoch_transition_suite.go b/integration/tests/epochs/dynamic_epoch_transition_suite.go index 828d688e74e..cec7c740acf 100644 --- a/integration/tests/epochs/dynamic_epoch_transition_suite.go +++ b/integration/tests/epochs/dynamic_epoch_transition_suite.go @@ -303,8 +303,9 @@ func (s *DynamicEpochTransitionSuite) newTestContainerOnNetwork(role flow.Role, } nodeConfig := testnet.NewNodeConfig(role, containerConfigs...) - testContainerConfig := testnet.NewContainerConfig(info.ContainerName, nodeConfig, info.NetworkingKey, info.StakingKey) - err := testContainerConfig.WriteKeyFiles(s.net.BootstrapDir, info.MachineAccountAddress, encodable.MachineAccountPrivKey{PrivateKey: info.MachineAccountKey}, role) + testContainerConfig, err := testnet.NewContainerConfig(info.ContainerName, nodeConfig, info.NetworkingKey, info.StakingKey) + require.NoError(s.T(), err) + err = testContainerConfig.WriteKeyFiles(s.net.BootstrapDir, info.MachineAccountAddress, encodable.MachineAccountPrivKey{PrivateKey: info.MachineAccountKey}, role) require.NoError(s.T(), err) //add our container to the network From bd97f2f024193e9a793b78a3f0a44980a84763c7 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 10 May 2024 01:50:05 +0800 Subject: [PATCH 12/13] update bootstrap examples and readme --- cmd/bootstrap/README.md | 8 ++++---- ...1fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595.json | 8 -------- ...fe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6.json | 9 +++++++++ 3 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.047e39d906e9ff961fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595.json create mode 100644 cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.79a7f711ba44dfdcfe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6.json diff --git a/cmd/bootstrap/README.md b/cmd/bootstrap/README.md index 8879c9c5f55..28caa0d34ca 100644 --- a/cmd/bootstrap/README.md +++ b/cmd/bootstrap/README.md @@ -18,9 +18,9 @@ Code structure: The bootstrapping will generate the following information: #### Per node -* Staking key (BLS key on curve BLS12-381) -* Networking key (ECDSA key on curve P-256) -* Random beacon key; _only_ for consensus nodes (BLS key on curve BLS12-381, used for a BLS-based threshold signatures) +* Staking private key (BLS key on curve BLS12-381) +* Networking private key (ECDSA key on curve P-256) +* Random beacon private key; _only_ for consensus nodes (BLS key on curve BLS12-381, used for a BLS-based threshold signatures) #### Node Identities * List of all authorized Flow nodes @@ -78,7 +78,7 @@ go run ./cmd/bootstrap key --address "example.com:1234" --role "consensus" -o ./ file needs to be available to respective partner node at boot up (or recovery after crash) * file `.node-info.pub.json` - public information - - file needs to be delivered to Dapper Labs for Phase 2 of generating root information, + - file needs to be delivered to the Flow Foundation team for Phase 2 of generating root information, but is not required at node start diff --git a/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.047e39d906e9ff961fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595.json b/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.047e39d906e9ff961fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595.json deleted file mode 100644 index 1c54ec0d6aa..00000000000 --- a/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.047e39d906e9ff961fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Role": "consensus", - "Address": "example.com", - "NodeID": "047e39d906e9ff961fa76cb5f479942d862c6cb1e768eb4525d6066cd707d595", - "Weight": 0, - "NetworkPubKey": "6fSXKlHhPLMqWo3nhBsyjTZlxGBl5HC4ZqC7p1z4GEHx48UKnDaKdz0QdOxzSJP2G+P3bzDiJdLbvEd1CSvVgA==", - "StakingPubKey": "gdQQp6cbOzc/pnhOMl8mNQTAsbkuGs78Q72/zmhrAK+Ii2c/v04F9CDEo+FuVc0eALL/T0ioZwaTFCBO9+JRjfakqOiBCI9b7Xj4E8Dv4vBHDQyLOBBqXeA2VLAJYgFL" -} diff --git a/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.79a7f711ba44dfdcfe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6.json b/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.79a7f711ba44dfdcfe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6.json new file mode 100644 index 00000000000..60f83924e4d --- /dev/null +++ b/cmd/bootstrap/example_files/partner-node-infos/public-genesis-information/node-info.pub.79a7f711ba44dfdcfe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6.json @@ -0,0 +1,9 @@ +{ + "Role": "consensus", + "Address": "example.com:1234", + "NodeID": "79a7f711ba44dfdcfe774769eb97af6af732337fbcf96b788dbe6c51d29c5ec6", + "Weight": 0, + "NetworkPubKey": "0162f95166d39db8a6486625813019fcc8bb3d9439ad1e57b44f7bf01235cbcabd66411c3faa98de806e439cb4372275b76dcd3af7d384d24851cbae89f92cda", + "StakingPubKey": "84f806be7e4db914358e5b66a405244161ad5bfd87939b3a9b428a941baa6ae245d0d7a6cef684bd7168815fda5e9b6506b2cc87ec9c52576913d1990fd7c376fc2c6884247ff6a7c0c46ca143e3697422913d53c134b9534a199b7fc8f57d50", + "StakingPoP": "oEz2R3qe86/ZaRAemZfpdjcBZcOt7RHLjMhqjf7gg99XMsaLjmDma94Rr9ylciti" +} \ No newline at end of file From 3cf9e32fc8873b54c7c7c2a3068a7c4ac9a7e489 Mon Sep 17 00:00:00 2001 From: Tarak Ben Youssef Date: Fri, 10 May 2024 01:50:39 +0800 Subject: [PATCH 13/13] simplify private key fixture --- .../hotstuff/timeoutcollector/aggregation_test.go | 6 +++--- .../verification/combined_signer_v3_test.go | 2 +- deploy/systemd-docker/example-node-infos.pub.json | 12 ++++++++---- engine/consensus/dkg/reactor_engine_test.go | 2 +- .../epochs/dynamic_epoch_transition_suite.go | 4 ++-- integration/utils/transactions.go | 2 +- module/epochs/machine_account_test.go | 4 ++-- utils/unittest/fixtures.go | 15 +++++++-------- 8 files changed, 25 insertions(+), 22 deletions(-) diff --git a/consensus/hotstuff/timeoutcollector/aggregation_test.go b/consensus/hotstuff/timeoutcollector/aggregation_test.go index c0beaf473fa..93eb0774d0a 100644 --- a/consensus/hotstuff/timeoutcollector/aggregation_test.go +++ b/consensus/hotstuff/timeoutcollector/aggregation_test.go @@ -40,7 +40,7 @@ func createAggregationData(t *testing.T, signersNumber int) ( pks := make([]crypto.PublicKey, 0, signersNumber) view := 10 + uint64(rand.Uint32()) for i := 0; i < signersNumber; i++ { - sk := unittest.PrivateKeyFixture(crypto.BLSBLS12381, crypto.KeyGenSeedMinLen) + sk := unittest.PrivateKeyFixture(crypto.BLSBLS12381) identity := unittest.IdentityFixture(unittest.WithStakingPubKey(sk.PublicKey())) // id ids = append(ids, &identity.IdentitySkeleton) @@ -70,7 +70,7 @@ func createAggregationData(t *testing.T, signersNumber int) ( func TestNewTimeoutSignatureAggregator(t *testing.T) { tag := "random_tag" - sk := unittest.PrivateKeyFixture(crypto.ECDSAP256, crypto.KeyGenSeedMinLen) + sk := unittest.PrivateKeyFixture(crypto.ECDSAP256) signer := unittest.IdentityFixture(unittest.WithStakingPubKey(sk.PublicKey())) // wrong key type _, err := NewTimeoutSignatureAggregator(0, flow.IdentitySkeletonList{&signer.IdentitySkeleton}, tag) @@ -191,7 +191,7 @@ func TestTimeoutSignatureAggregator_Aggregate(t *testing.T) { var err error aggregator, ids, pks, sigs, signersInfo, msgs, hashers := createAggregationData(t, signersNum) // replace sig with random one - sk := unittest.PrivateKeyFixture(crypto.BLSBLS12381, crypto.KeyGenSeedMinLen) + sk := unittest.PrivateKeyFixture(crypto.BLSBLS12381) sigs[0], err = sk.Sign([]byte("dummy"), hashers[0]) require.NoError(t, err) diff --git a/consensus/hotstuff/verification/combined_signer_v3_test.go b/consensus/hotstuff/verification/combined_signer_v3_test.go index eaab5d6ac47..b2195e0c575 100644 --- a/consensus/hotstuff/verification/combined_signer_v3_test.go +++ b/consensus/hotstuff/verification/combined_signer_v3_test.go @@ -339,7 +339,7 @@ func generateAggregatedSignature(t *testing.T, n int, msg []byte, tag string) ([ // generateSignature creates a single private BLS 12-381 key, signs the provided `message` with // using domain separation `tag` and return the private key and signature. func generateSignature(t *testing.T, message []byte, tag string) (crypto.PrivateKey, crypto.Signature) { - priv := unittest.PrivateKeyFixture(crypto.BLSBLS12381, crypto.KeyGenSeedMinLen) + priv := unittest.PrivateKeyFixture(crypto.BLSBLS12381) sig, err := priv.Sign(message, msig.NewBLSHasher(tag)) require.NoError(t, err) return priv, sig diff --git a/deploy/systemd-docker/example-node-infos.pub.json b/deploy/systemd-docker/example-node-infos.pub.json index cfba2da97fc..a60dcf48625 100644 --- a/deploy/systemd-docker/example-node-infos.pub.json +++ b/deploy/systemd-docker/example-node-infos.pub.json @@ -5,7 +5,8 @@ "NodeID": "9bbea0644b5e91ec66b4afddef7083e896da545d530ee52b85c78afa88301556", "Weight": 1000, "NetworkPubKey": "...", - "StakingPubKey": "..." + "StakingPubKey": "...", + "StakingKeyPoP": "..." }, { "Role": "consensus", @@ -13,7 +14,8 @@ "NodeID": "9bbea0644b5e91ec66b4afddef7083e896da545d530ee52b85c78afa88301556", "Weight": 1000, "NetworkPubKey": "...", - "StakingPubKey": "..." + "StakingPubKey": "...", + "StakingKeyPoP": "..." }, { "Role": "execution", @@ -21,7 +23,8 @@ "NodeID": "9bbea0644b5e91ec66b4afddef7083e896da545d530ee52b85c78afa88301556", "Weight": 1000, "NetworkPubKey": "...", - "StakingPubKey": "..." + "StakingPubKey": "...", + "StakingKeyPoP": "..." }, { "Role": "verification", @@ -29,6 +32,7 @@ "NodeID": "9bbea0644b5e91ec66b4afddef7083e896da545d530ee52b85c78afa88301556", "Weight": 1000, "NetworkPubKey": "...", - "StakingPubKey": "..." + "StakingPubKey": "...", + "StakingKeyPoP": "..." } ] diff --git a/engine/consensus/dkg/reactor_engine_test.go b/engine/consensus/dkg/reactor_engine_test.go index b484fe503ee..bca7caaee01 100644 --- a/engine/consensus/dkg/reactor_engine_test.go +++ b/engine/consensus/dkg/reactor_engine_test.go @@ -114,7 +114,7 @@ func (suite *ReactorEngineSuite_SetupPhase) SetupTest() { // expectedPrivKey is the expected private share produced by the dkg run. We // will mock the controller to return this value, and we will check it // against the value that gets inserted in the DB at the end. - suite.expectedPrivateKey = unittest.PrivateKeyFixture(crypto.BLSBLS12381, 48) + suite.expectedPrivateKey = unittest.PrivateKeyFixture(crypto.BLSBLS12381) // mock protocol state suite.currentEpoch = new(protocol.Epoch) diff --git a/integration/tests/epochs/dynamic_epoch_transition_suite.go b/integration/tests/epochs/dynamic_epoch_transition_suite.go index cec7c740acf..bb8b29fde32 100644 --- a/integration/tests/epochs/dynamic_epoch_transition_suite.go +++ b/integration/tests/epochs/dynamic_epoch_transition_suite.go @@ -165,13 +165,13 @@ func (s *DynamicEpochTransitionSuite) generateAccountKeys(role flow.Role) ( machineAccountKey crypto.PrivateKey, machineAccountPubKey *sdk.AccountKey, ) { - operatorAccountKey = unittest.PrivateKeyFixture(crypto.ECDSAP256, crypto.KeyGenSeedMinLen) + operatorAccountKey = unittest.PrivateKeyFixture(crypto.ECDSAP256) networkingKey = unittest.NetworkingPrivKeyFixture() stakingKey = unittest.StakingPrivKeyFixture() // create a machine account if role == flow.RoleConsensus || role == flow.RoleCollection { - machineAccountKey = unittest.PrivateKeyFixture(crypto.ECDSAP256, crypto.KeyGenSeedMinLen) + machineAccountKey = unittest.PrivateKeyFixture(crypto.ECDSAP256) machineAccountPubKey = &sdk.AccountKey{ PublicKey: machineAccountKey.PublicKey(), diff --git a/integration/utils/transactions.go b/integration/utils/transactions.go index 1eaef320b68..4c804290240 100644 --- a/integration/utils/transactions.go +++ b/integration/utils/transactions.go @@ -218,7 +218,7 @@ func MakeSetProtocolStateVersionTx( // This ensures a single transaction can be sealed by the network. func CreateFlowAccount(ctx context.Context, client *testnet.Client) (sdk.Address, error) { fullAccountKey := sdk.NewAccountKey(). - SetPublicKey(unittest.PrivateKeyFixture(crypto.ECDSAP256, crypto.KeyGenSeedMinLen).PublicKey()). + SetPublicKey(unittest.PrivateKeyFixture(crypto.ECDSAP256).PublicKey()). SetHashAlgo(sdkcrypto.SHA2_256). SetWeight(sdk.AccountKeyWeightThreshold) diff --git a/module/epochs/machine_account_test.go b/module/epochs/machine_account_test.go index 0eb5c593bcf..e80af3e31ce 100644 --- a/module/epochs/machine_account_test.go +++ b/module/epochs/machine_account_test.go @@ -36,7 +36,7 @@ func TestMachineAccountChecking(t *testing.T) { }) t.Run("inconsistent key", func(t *testing.T) { local, remote := unittest.MachineAccountFixture(t) - randomKey := unittest.PrivateKeyFixture(crypto.ECDSAP256, unittest.DefaultSeedFixtureLength) + randomKey := unittest.PrivateKeyFixture(crypto.ECDSAP256) remote.Keys[0].PublicKey = randomKey.PublicKey() err := CheckMachineAccountInfo(zerolog.Nop(), conf, flow.RoleConsensus, local, remote) require.Error(t, err) @@ -154,7 +154,7 @@ func TestMachineAccountChecking(t *testing.T) { local, remote := unittest.MachineAccountFixture(t) // non-standard sig algo - sk := unittest.PrivateKeyFixture(crypto.ECDSASecp256k1, unittest.DefaultSeedFixtureLength) + sk := unittest.PrivateKeyFixture(crypto.ECDSASecp256k1) local.EncodedPrivateKey = sk.Encode() local.SigningAlgorithm = crypto.ECDSASecp256k1 // consistent between local/remote diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 08158b916b1..6657cdfcc6d 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -52,8 +52,7 @@ import ( ) const ( - DefaultSeedFixtureLength = 64 - DefaultAddress = "localhost:0" + DefaultAddress = "localhost:0" ) // returns a deterministic math/rand PRG that can be used for deterministic randomness in tests only. @@ -2404,9 +2403,9 @@ func DKGBroadcastMessageFixture() *messages.BroadcastDKGMessage { } } -// PrivateKeyFixture returns a random private key with specified signature algorithm and seed length -func PrivateKeyFixture(algo crypto.SigningAlgorithm, seedLength int) crypto.PrivateKey { - sk, err := crypto.GeneratePrivateKey(algo, SeedFixture(seedLength)) +// PrivateKeyFixture returns a random private key with specified signature algorithm +func PrivateKeyFixture(algo crypto.SigningAlgorithm) crypto.PrivateKey { + sk, err := crypto.GeneratePrivateKey(algo, SeedFixture(crypto.KeyGenSeedMinLen)) if err != nil { panic(err) } @@ -2435,18 +2434,18 @@ func StakingPrivKeyByIdentifier(id flow.Identifier) crypto.PrivateKey { // NetworkingPrivKeyFixture returns random ECDSAP256 private key func NetworkingPrivKeyFixture() crypto.PrivateKey { - return PrivateKeyFixture(crypto.ECDSAP256, crypto.KeyGenSeedMinLen) + return PrivateKeyFixture(crypto.ECDSAP256) } // StakingPrivKeyFixture returns a random BLS12381 private keyf func StakingPrivKeyFixture() crypto.PrivateKey { - return PrivateKeyFixture(crypto.BLSBLS12381, crypto.KeyGenSeedMinLen) + return PrivateKeyFixture(crypto.BLSBLS12381) } func NodeMachineAccountInfoFixture() bootstrap.NodeMachineAccountInfo { return bootstrap.NodeMachineAccountInfo{ Address: RandomAddressFixture().String(), - EncodedPrivateKey: PrivateKeyFixture(crypto.ECDSAP256, DefaultSeedFixtureLength).Encode(), + EncodedPrivateKey: PrivateKeyFixture(crypto.ECDSAP256).Encode(), HashAlgorithm: bootstrap.DefaultMachineAccountHashAlgo, SigningAlgorithm: bootstrap.DefaultMachineAccountSignAlgo, KeyIndex: bootstrap.DefaultMachineAccountKeyIndex,