From f1522f8b23ef1a5450e626d187accac6bc637eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Fran=C3=A7a?= Date: Fri, 13 Dec 2024 16:29:37 +0000 Subject: [PATCH 1/2] feat: Added tool to recover public keys from secret keys (#224) --- node/tools/src/bin/keys.rs | 4 +-- node/tools/src/bin/keys_recovery.rs | 43 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 node/tools/src/bin/keys_recovery.rs diff --git a/node/tools/src/bin/keys.rs b/node/tools/src/bin/keys.rs index d7b0e29f..8b751c45 100644 --- a/node/tools/src/bin/keys.rs +++ b/node/tools/src/bin/keys.rs @@ -1,11 +1,11 @@ -//! This tool generates a validator key pair and prints it to stdout. +//! This tool generates a validator/attester/node key pair and prints it to stdout. #![allow(clippy::print_stdout)] use crypto::TextFmt as _; use zksync_consensus_crypto as crypto; use zksync_consensus_roles::{attester, node, validator}; -/// This tool generates a validator key pair and prints it to stdout. +/// This tool generates a validator/attester/node key pair and prints it to stdout. fn main() { let validator_key = validator::SecretKey::generate(); let attester_key = attester::SecretKey::generate(); diff --git a/node/tools/src/bin/keys_recovery.rs b/node/tools/src/bin/keys_recovery.rs new file mode 100644 index 00000000..1bae3747 --- /dev/null +++ b/node/tools/src/bin/keys_recovery.rs @@ -0,0 +1,43 @@ +//! This tool calculates a node/validator public key from its corresponding secret key and prints it to stdout. +#![allow(clippy::print_stdout)] + +use crypto::TextFmt as _; +use std::io; +use zksync_consensus_crypto as crypto; +use zksync_consensus_roles::{node, validator}; + +/// This tool calculates a node/validator public key from its corresponding secret key and prints it to stdout. +fn main() { + println!("Please enter the node secret key (don't trim the identifiers at the beginning) or leave empty to skip:"); + + let mut input = String::new(); + + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + + let input = input.trim(); + + if !input.is_empty() { + let text = crypto::Text::new(input); + let secret_key = node::SecretKey::decode(text).expect("Failed to decode the secret key"); + println!("{}", secret_key.public().encode()); + } + + println!("Please enter the validator secret key (don't trim the identifiers at the beginning) or leave empty to skip:"); + + let mut input = String::new(); + + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + + let input = input.trim(); + + if !input.is_empty() { + let text = crypto::Text::new(input); + let secret_key = + validator::SecretKey::decode(text).expect("Failed to decode the secret key"); + println!("{}", secret_key.public().encode()); + } +} From f07fcfa67e298d53ddeb801ce20c3ea2571e92da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Fran=C3=A7a?= Date: Fri, 13 Dec 2024 20:08:52 +0000 Subject: [PATCH 2/2] feat: Added view timeout duration as a config parameter (#222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ The hardcoded 2s view timeout was causing problems in Era testnet because the block time there is longer (>5s). View timeout needs to be a configurable parameter since different chains will want different block times. --- node/components/bft/src/chonky_bft/mod.rs | 5 +---- node/components/bft/src/chonky_bft/new_view.rs | 4 ++-- node/components/bft/src/chonky_bft/proposer.rs | 5 ++--- node/components/bft/src/chonky_bft/testonly.rs | 7 ++++++- node/components/bft/src/config.rs | 3 +++ node/components/bft/src/testonly/node.rs | 2 ++ node/components/executor/src/lib.rs | 3 +++ node/components/executor/src/tests.rs | 1 + node/tools/build.rs | 5 ++++- node/tools/src/bin/deployer.rs | 2 ++ node/tools/src/bin/localnet_config.rs | 2 ++ node/tools/src/config.rs | 6 +++++- node/tools/src/proto/mod.proto | 4 ++++ node/tools/src/tests.rs | 1 + 14 files changed, 38 insertions(+), 12 deletions(-) diff --git a/node/components/bft/src/chonky_bft/mod.rs b/node/components/bft/src/chonky_bft/mod.rs index 2d6a25c4..834e5f2d 100644 --- a/node/components/bft/src/chonky_bft/mod.rs +++ b/node/components/bft/src/chonky_bft/mod.rs @@ -18,9 +18,6 @@ pub(crate) mod testonly; mod tests; mod timeout; -/// The duration of the view timeout. -pub(crate) const VIEW_TIMEOUT_DURATION: time::Duration = time::Duration::milliseconds(2000); - /// The StateMachine struct contains the state of the replica and implements all the /// logic of ChonkyBFT. #[derive(Debug)] @@ -90,6 +87,7 @@ impl StateMachine { } let this = Self { + view_timeout: time::Deadline::Finite(ctx.now() + config.view_timeout), config, outbound_channel, inbound_channel, @@ -104,7 +102,6 @@ impl StateMachine { commit_qcs_cache: BTreeMap::new(), timeout_views_cache: BTreeMap::new(), timeout_qcs_cache: BTreeMap::new(), - view_timeout: time::Deadline::Finite(ctx.now() + VIEW_TIMEOUT_DURATION), view_start: ctx.now(), }; diff --git a/node/components/bft/src/chonky_bft/new_view.rs b/node/components/bft/src/chonky_bft/new_view.rs index 21ddb65a..ff99f69e 100644 --- a/node/components/bft/src/chonky_bft/new_view.rs +++ b/node/components/bft/src/chonky_bft/new_view.rs @@ -1,5 +1,5 @@ use super::StateMachine; -use crate::{chonky_bft::VIEW_TIMEOUT_DURATION, metrics}; +use crate::metrics; use zksync_concurrency::{ctx, error::Wrap, metrics::LatencyHistogramExt as _, time}; use zksync_consensus_network::io::ConsensusInputMessage; use zksync_consensus_roles::validator; @@ -155,7 +155,7 @@ impl StateMachine { self.view_start = now; // Reset the timeout. - self.view_timeout = time::Deadline::Finite(ctx.now() + VIEW_TIMEOUT_DURATION); + self.view_timeout = time::Deadline::Finite(ctx.now() + self.config.view_timeout); Ok(()) } diff --git a/node/components/bft/src/chonky_bft/proposer.rs b/node/components/bft/src/chonky_bft/proposer.rs index ad88d854..2766912e 100644 --- a/node/components/bft/src/chonky_bft/proposer.rs +++ b/node/components/bft/src/chonky_bft/proposer.rs @@ -1,4 +1,3 @@ -use super::VIEW_TIMEOUT_DURATION; use crate::{metrics, Config, ToNetworkMessage}; use std::sync::Arc; use zksync_concurrency::{ctx, error::Wrap as _, sync}; @@ -27,7 +26,7 @@ pub(crate) async fn run_proposer( // Create a proposal for the given justification, within the timeout. let proposal = match create_proposal( - &ctx.with_timeout(VIEW_TIMEOUT_DURATION), + &ctx.with_timeout(cfg.view_timeout), cfg.clone(), justification, ) @@ -35,7 +34,7 @@ pub(crate) async fn run_proposer( { Ok(proposal) => proposal, Err(ctx::Error::Canceled(_)) => { - tracing::error!("run_proposer(): timed out while creating a proposal"); + tracing::warn!("run_proposer(): timed out while creating a proposal"); continue; } Err(ctx::Error::Internal(err)) => { diff --git a/node/components/bft/src/chonky_bft/testonly.rs b/node/components/bft/src/chonky_bft/testonly.rs index 7aba4cd7..948369c1 100644 --- a/node/components/bft/src/chonky_bft/testonly.rs +++ b/node/components/bft/src/chonky_bft/testonly.rs @@ -6,7 +6,11 @@ use crate::{ }; use assert_matches::assert_matches; use std::sync::Arc; -use zksync_concurrency::{ctx, sync, sync::prunable_mpsc}; +use zksync_concurrency::{ + ctx, + sync::{self, prunable_mpsc}, + time, +}; use zksync_consensus_roles::validator; use zksync_consensus_storage::{ testonly::{in_memory, TestMemoryStorage}, @@ -70,6 +74,7 @@ impl UTHarness { replica_store: Box::new(in_memory::ReplicaStore::default()), payload_manager, max_payload_size: MAX_PAYLOAD_SIZE, + view_timeout: time::Duration::milliseconds(2000), }); let replica = StateMachine::start( ctx, diff --git a/node/components/bft/src/config.rs b/node/components/bft/src/config.rs index ea83d2fe..9aa5dbb4 100644 --- a/node/components/bft/src/config.rs +++ b/node/components/bft/src/config.rs @@ -1,6 +1,7 @@ //! The inner data of the consensus state machine. This is shared between the different roles. use crate::PayloadManager; use std::sync::Arc; +use zksync_concurrency::time; use zksync_consensus_roles::validator; use zksync_consensus_storage as storage; @@ -12,6 +13,8 @@ pub struct Config { /// The maximum size of the payload of a block, in bytes. We will /// reject blocks with payloads larger than this. pub max_payload_size: usize, + /// The duration of the view timeout. + pub view_timeout: time::Duration, /// Block store. pub block_store: Arc, /// Replica store. diff --git a/node/components/bft/src/testonly/node.rs b/node/components/bft/src/testonly/node.rs index 8296274a..25030ed4 100644 --- a/node/components/bft/src/testonly/node.rs +++ b/node/components/bft/src/testonly/node.rs @@ -1,6 +1,7 @@ use crate::{testonly, FromNetworkMessage, PayloadManager, ToNetworkMessage}; use anyhow::Context as _; use std::sync::Arc; +use zksync_concurrency::time; use zksync_concurrency::{ctx, ctx::channel, scope, sync}; use zksync_consensus_network as network; use zksync_consensus_storage as storage; @@ -58,6 +59,7 @@ impl Node { replica_store: Box::new(in_memory::ReplicaStore::default()), payload_manager: self.behavior.payload_manager(), max_payload_size: MAX_PAYLOAD_SIZE, + view_timeout: time::Duration::milliseconds(2000), } .run(ctx, net_send, consensus_receiver) .await diff --git a/node/components/executor/src/lib.rs b/node/components/executor/src/lib.rs index 7fb27892..4f025c3d 100644 --- a/node/components/executor/src/lib.rs +++ b/node/components/executor/src/lib.rs @@ -39,6 +39,8 @@ pub struct Config { pub public_addr: net::Host, /// Maximal size of the block payload. pub max_payload_size: usize, + /// The duration of the view timeout, in milliseconds. + pub view_timeout: time::Duration, /// Key of this node. It uniquely identifies the node. /// It should match the secret key provided in the `node_key` file. pub node_key: node::SecretKey, @@ -157,6 +159,7 @@ impl Executor { replica_store: validator.replica_store, payload_manager: validator.payload_manager, max_payload_size: self.config.max_payload_size, + view_timeout: self.config.view_timeout, } .run(ctx, network_send, consensus_recv) .await diff --git a/node/components/executor/src/tests.rs b/node/components/executor/src/tests.rs index e6ac8861..3c3fcee3 100644 --- a/node/components/executor/src/tests.rs +++ b/node/components/executor/src/tests.rs @@ -18,6 +18,7 @@ fn config(cfg: &network::Config) -> Config { server_addr: *cfg.server_addr, public_addr: cfg.public_addr.clone(), max_payload_size: usize::MAX, + view_timeout: time::Duration::milliseconds(1000), node_key: cfg.gossip.key.clone(), gossip_dynamic_inbound_limit: cfg.gossip.dynamic_inbound_limit, gossip_static_inbound: cfg.gossip.static_inbound.clone(), diff --git a/node/tools/build.rs b/node/tools/build.rs index f4cfa5df..b4d62f2c 100644 --- a/node/tools/build.rs +++ b/node/tools/build.rs @@ -3,7 +3,10 @@ fn main() { zksync_protobuf_build::Config { input_root: "src/proto".into(), proto_root: "zksync/tools".into(), - dependencies: vec!["::zksync_consensus_roles::proto".parse().unwrap()], + dependencies: vec![ + "::zksync_protobuf::proto".parse().unwrap(), + "::zksync_consensus_roles::proto".parse().unwrap(), + ], protobuf_crate: "::zksync_protobuf".parse().unwrap(), is_public: false, } diff --git a/node/tools/src/bin/deployer.rs b/node/tools/src/bin/deployer.rs index bd5ee64f..addbffc2 100644 --- a/node/tools/src/bin/deployer.rs +++ b/node/tools/src/bin/deployer.rs @@ -4,6 +4,7 @@ use std::{ collections::HashMap, net::{Ipv4Addr, SocketAddr}, }; +use zksync_concurrency::time; use zksync_consensus_roles::{node::SecretKey, validator}; use zksync_consensus_tools::{config, k8s, k8s::ConsensusNode}; @@ -48,6 +49,7 @@ fn generate_consensus_nodes(nodes: usize, seed_nodes_amount: Option) -> V metrics_server_addr: None, genesis: setup.genesis.clone(), max_payload_size: 1000000, + view_timeout: time::Duration::milliseconds(2000), validator_key: Some(validator_keys[i].clone()), attester_key: Some(attester_keys[i].clone()), node_key: node_keys[i].clone(), diff --git a/node/tools/src/bin/localnet_config.rs b/node/tools/src/bin/localnet_config.rs index 0769a62f..a35841f4 100644 --- a/node/tools/src/bin/localnet_config.rs +++ b/node/tools/src/bin/localnet_config.rs @@ -9,6 +9,7 @@ use std::{ os::unix::fs::PermissionsExt, path::PathBuf, }; +use zksync_concurrency::time; use zksync_consensus_roles::{node, validator}; use zksync_consensus_tools::config; use zksync_protobuf::serde::Serialize; @@ -76,6 +77,7 @@ fn main() -> anyhow::Result<()> { .map(|port| SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port)), genesis: setup.genesis.clone(), max_payload_size: 1000000, + view_timeout: time::Duration::milliseconds(2000), node_key: node_keys[i].clone(), validator_key: validator_keys.get(i).cloned(), attester_key: attester_keys.get(i).cloned(), diff --git a/node/tools/src/config.rs b/node/tools/src/config.rs index 8399e036..b8cef4b7 100644 --- a/node/tools/src/config.rs +++ b/node/tools/src/config.rs @@ -9,7 +9,7 @@ use std::{ sync::Arc, }; use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use zksync_concurrency::{ctx, net}; +use zksync_concurrency::{ctx, net, time}; use zksync_consensus_bft as bft; use zksync_consensus_crypto::{read_optional_text, read_required_text, Text, TextFmt}; use zksync_consensus_executor::{self as executor, attestation}; @@ -76,6 +76,7 @@ pub struct App { pub genesis: validator::Genesis, pub max_payload_size: usize, + pub view_timeout: time::Duration, pub validator_key: Option, pub attester_key: Option, @@ -176,6 +177,7 @@ impl ProtoFmt for App { genesis: read_required(&r.genesis).context("genesis")?, max_payload_size, + view_timeout: read_required(&r.view_timeout).context("view_timeout")?, // TODO: read secret. validator_key: read_optional_secret_text(&r.validator_secret_key) .context("validator_secret_key")?, @@ -201,6 +203,7 @@ impl ProtoFmt for App { genesis: Some(self.genesis.build()), max_payload_size: Some(self.max_payload_size.try_into().unwrap()), + view_timeout: Some(self.view_timeout.build()), validator_secret_key: self.validator_key.as_ref().map(TextFmt::encode), attester_secret_key: self.attester_key.as_ref().map(TextFmt::encode), @@ -270,6 +273,7 @@ impl Configs { gossip_static_inbound: self.app.gossip_static_inbound.clone(), gossip_static_outbound: self.app.gossip_static_outbound.clone(), max_payload_size: self.app.max_payload_size, + view_timeout: self.app.view_timeout, rpc: executor::RpcConfig::default(), debug_page: self .app diff --git a/node/tools/src/proto/mod.proto b/node/tools/src/proto/mod.proto index 95091d77..5e9c85a1 100644 --- a/node/tools/src/proto/mod.proto +++ b/node/tools/src/proto/mod.proto @@ -41,6 +41,7 @@ syntax = "proto3"; package zksync.tools; import "zksync/roles/validator.proto"; +import "zksync/std.proto"; // (public key, ip address) of a gossip network node. message NodeAddr { @@ -98,6 +99,9 @@ message AppConfig { // Maximal size of the block payload. optional uint64 max_payload_size = 5; // required; bytes + // The duration of the view timeout. + optional std.Duration view_timeout = 19; // required; milliseconds + // Validator secret key. optional string validator_secret_key = 10; // optional; ValidatorSecretKey diff --git a/node/tools/src/tests.rs b/node/tools/src/tests.rs index 9381700c..1db81400 100644 --- a/node/tools/src/tests.rs +++ b/node/tools/src/tests.rs @@ -18,6 +18,7 @@ impl Distribution for EncodeDist { genesis: rng.gen(), max_payload_size: rng.gen(), + view_timeout: self.sample(rng), validator_key: self.sample_opt(|| rng.gen()), attester_key: self.sample_opt(|| rng.gen()),