Skip to content

Commit

Permalink
feat: added seed_peers to consensus global config (#2920)
Browse files Browse the repository at this point in the history
It will allow us to announce the recommended default list of peers to
all ENs without manual intervention.
Fixes BFT-509.

---------

Co-authored-by: Bruno França <bruno@franca.xyz>
  • Loading branch information
pompon0 and brunoffranca authored Sep 20, 2024
1 parent 38fc824 commit e9d1d90
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 27 deletions.
2 changes: 2 additions & 0 deletions core/lib/config/src/configs/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub struct GenesisSpec {
pub leader: ValidatorPublicKey,
/// Address of the registry contract.
pub registry_address: Option<ethabi::Address>,
/// Recommended list of peers to connect to.
pub seed_peers: BTreeMap<NodePublicKey, Host>,
}

#[derive(Clone, Debug, PartialEq, Default)]
Expand Down
8 changes: 7 additions & 1 deletion core/lib/config/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,14 +774,20 @@ impl Distribution<configs::consensus::WeightedAttester> for EncodeDist {

impl Distribution<configs::consensus::GenesisSpec> for EncodeDist {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> configs::consensus::GenesisSpec {
use configs::consensus::{GenesisSpec, ProtocolVersion, ValidatorPublicKey};
use configs::consensus::{
GenesisSpec, Host, NodePublicKey, ProtocolVersion, ValidatorPublicKey,
};
GenesisSpec {
chain_id: L2ChainId::default(),
protocol_version: ProtocolVersion(self.sample(rng)),
validators: self.sample_collect(rng),
attesters: self.sample_collect(rng),
leader: ValidatorPublicKey(self.sample(rng)),
registry_address: self.sample_opt(|| rng.gen()),
seed_peers: self
.sample_range(rng)
.map(|_| (NodePublicKey(self.sample(rng)), Host(self.sample(rng))))
.collect(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core/lib/dal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ zksync_utils.workspace = true
zksync_system_constants.workspace = true
zksync_contracts.workspace = true
zksync_types.workspace = true
zksync_concurrency.workspace = true
zksync_consensus_roles.workspace = true
zksync_consensus_storage.workspace = true
zksync_protobuf.workspace = true
Expand Down
34 changes: 33 additions & 1 deletion core/lib/dal/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ mod testonly;
#[cfg(test)]
mod tests;

use std::collections::BTreeMap;

use anyhow::{anyhow, Context as _};
use zksync_consensus_roles::{attester, validator};
use zksync_concurrency::net;
use zksync_consensus_roles::{attester, node, validator};
use zksync_protobuf::{read_required, required, ProtoFmt, ProtoRepr};
use zksync_types::{
abi, ethabi,
Expand All @@ -27,6 +30,23 @@ use crate::models::{parse_h160, parse_h256};
pub struct GlobalConfig {
pub genesis: validator::Genesis,
pub registry_address: Option<ethabi::Address>,
pub seed_peers: BTreeMap<node::PublicKey, net::Host>,
}

impl ProtoRepr for proto::NodeAddr {
type Type = (node::PublicKey, net::Host);
fn read(&self) -> anyhow::Result<Self::Type> {
Ok((
read_required(&self.key).context("key")?,
net::Host(required(&self.addr).context("addr")?.clone()),
))
}
fn build(this: &Self::Type) -> Self {
Self {
key: Some(this.0.build()),
addr: Some(this.1 .0.clone()),
}
}
}

impl ProtoFmt for GlobalConfig {
Expand All @@ -41,13 +61,25 @@ impl ProtoFmt for GlobalConfig {
.map(|a| parse_h160(a))
.transpose()
.context("registry_address")?,
seed_peers: r
.seed_peers
.iter()
.enumerate()
.map(|(i, e)| e.read().context(i))
.collect::<Result<_, _>>()
.context("seed_peers")?,
})
}

fn build(&self) -> Self::Proto {
Self::Proto {
genesis: Some(self.genesis.build()),
registry_address: self.registry_address.map(|a| a.as_bytes().to_vec()),
seed_peers: self
.seed_peers
.iter()
.map(|(k, v)| ProtoRepr::build(&(k.clone(), v.clone())))
.collect(),
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions core/lib/dal/src/consensus/proto/mod.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package zksync.dal;

import "zksync/roles/validator.proto";
import "zksync/roles/attester.proto";
import "zksync/roles/node.proto";

message Payload {
// zksync-era ProtocolVersionId
Expand Down Expand Up @@ -122,9 +123,15 @@ message AttesterCommittee {
repeated roles.attester.WeightedAttester members = 1; // required
}

message NodeAddr {
optional roles.node.PublicKey key = 1; // required
optional string addr = 2; // required; Host
}

message GlobalConfig {
optional roles.validator.Genesis genesis = 1; // required
optional bytes registry_address = 2; // optional; H160
repeated NodeAddr seed_peers = 3;
}

message AttestationStatus {
Expand Down
4 changes: 4 additions & 0 deletions core/lib/dal/src/consensus_dal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl ConsensusDal<'_, '_> {
return Ok(Some(GlobalConfig {
genesis,
registry_address: None,
seed_peers: [].into(),
}));
}
Ok(None)
Expand Down Expand Up @@ -184,6 +185,7 @@ impl ConsensusDal<'_, '_> {
}
.with_hash(),
registry_address: old.registry_address,
seed_peers: old.seed_peers,
};
txn.consensus_dal().try_update_global_config(&new).await?;
txn.commit().await?;
Expand Down Expand Up @@ -681,6 +683,7 @@ mod tests {
let cfg = GlobalConfig {
genesis: genesis.with_hash(),
registry_address: Some(rng.gen()),
seed_peers: [].into(), // TODO: rng.gen() for Host
};
conn.consensus_dal()
.try_update_global_config(&cfg)
Expand Down Expand Up @@ -715,6 +718,7 @@ mod tests {
let cfg = GlobalConfig {
genesis: setup.genesis.clone(),
registry_address: Some(rng.gen()),
seed_peers: [].into(),
};
conn.consensus_dal()
.try_update_global_config(&cfg)
Expand Down
44 changes: 32 additions & 12 deletions core/lib/protobuf_config/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ impl ProtoRepr for proto::GenesisSpec {
.map(|x| parse_h160(x))
.transpose()
.context("registry_address")?,
seed_peers: self
.seed_peers
.iter()
.enumerate()
.map(|(i, e)| e.read().context(i))
.collect::<Result<_, _>>()
.context("seed_peers")?,
})
}
fn build(this: &Self::Type) -> Self {
Expand All @@ -81,6 +88,11 @@ impl ProtoRepr for proto::GenesisSpec {
attesters: this.attesters.iter().map(ProtoRepr::build).collect(),
leader: Some(this.leader.0.clone()),
registry_address: this.registry_address.map(|a| format!("{:?}", a)),
seed_peers: this
.seed_peers
.iter()
.map(|(k, v)| proto::NodeAddr::build(&(k.clone(), v.clone())))
.collect(),
}
}
}
Expand All @@ -99,15 +111,25 @@ impl ProtoRepr for proto::RpcConfig {
}
}

impl ProtoRepr for proto::NodeAddr {
type Type = (NodePublicKey, Host);
fn read(&self) -> anyhow::Result<Self::Type> {
Ok((
NodePublicKey(required(&self.key).context("key")?.clone()),
Host(required(&self.addr).context("addr")?.clone()),
))
}
fn build(this: &Self::Type) -> Self {
Self {
key: Some(this.0 .0.clone()),
addr: Some(this.1 .0.clone()),
}
}
}

impl ProtoRepr for proto::Config {
type Type = ConsensusConfig;
fn read(&self) -> anyhow::Result<Self::Type> {
let read_addr = |e: &proto::NodeAddr| {
let key = NodePublicKey(required(&e.key).context("key")?.clone());
let addr = Host(required(&e.addr).context("addr")?.clone());
anyhow::Ok((key, addr))
};

let max_payload_size = required(&self.max_payload_size)
.and_then(|x| Ok((*x).try_into()?))
.context("max_payload_size")?;
Expand Down Expand Up @@ -144,8 +166,9 @@ impl ProtoRepr for proto::Config {
.gossip_static_outbound
.iter()
.enumerate()
.map(|(i, e)| read_addr(e).context(i))
.collect::<Result<_, _>>()?,
.map(|(i, e)| e.read().context(i))
.collect::<Result<_, _>>()
.context("gossip_static_outbound")?,
genesis_spec: read_optional_repr(&self.genesis_spec),
rpc: read_optional_repr(&self.rpc_config),
})
Expand All @@ -168,10 +191,7 @@ impl ProtoRepr for proto::Config {
gossip_static_outbound: this
.gossip_static_outbound
.iter()
.map(|x| proto::NodeAddr {
key: Some(x.0 .0.clone()),
addr: Some(x.1 .0.clone()),
})
.map(|(k, v)| proto::NodeAddr::build(&(k.clone(), v.clone())))
.collect(),
genesis_spec: this.genesis_spec.as_ref().map(ProtoRepr::build),
rpc_config: this.rpc.as_ref().map(ProtoRepr::build),
Expand Down
5 changes: 3 additions & 2 deletions core/lib/protobuf_config/src/proto/core/consensus.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ package zksync.core.consensus;

import "zksync/std.proto";

// (public key, ip address) of a gossip network node.
// (public key, host address) of a gossip network node.
message NodeAddr {
optional string key = 1; // required; NodePublicKey
optional string addr = 2; // required; IpAddr
optional string addr = 2; // required; Host
}

// Weighted member of a validator committee.
Expand All @@ -58,6 +58,7 @@ message GenesisSpec {
repeated WeightedAttester attesters = 5; // can be empty; attester committee.
// Currently not in consensus genesis, but still a part of the global configuration.
optional string registry_address = 6; // optional; H160
repeated NodeAddr seed_peers = 7;
}

// Per peer connection RPC rate limits.
Expand Down
28 changes: 26 additions & 2 deletions core/node/consensus/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Configuration utilities for the consensus component.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};

use anyhow::Context as _;
use secrecy::{ExposeSecret as _, Secret};
Expand Down Expand Up @@ -44,6 +44,7 @@ pub(super) struct GenesisSpec {
pub(super) attesters: Option<attester::Committee>,
pub(super) leader_selection: validator::LeaderSelectionMode,
pub(super) registry_address: Option<ethabi::Address>,
pub(super) seed_peers: BTreeMap<node::PublicKey, net::Host>,
}

impl GenesisSpec {
Expand All @@ -55,6 +56,7 @@ impl GenesisSpec {
attesters: cfg.genesis.attesters.clone(),
leader_selection: cfg.genesis.leader_selection.clone(),
registry_address: cfg.registry_address,
seed_peers: cfg.seed_peers.clone(),
}
}

Expand Down Expand Up @@ -98,6 +100,19 @@ impl GenesisSpec {
Some(attester::Committee::new(attesters).context("attesters")?)
},
registry_address: x.registry_address,
seed_peers: x
.seed_peers
.iter()
.map(|(key, addr)| {
anyhow::Ok((
Text::new(&key.0)
.decode::<node::PublicKey>()
.context("key")?,
net::Host(addr.0.clone()),
))
})
.collect::<Result<_, _>>()
.context("seed_peers")?,
})
}
}
Expand All @@ -109,9 +124,18 @@ pub(super) fn node_key(secrets: &ConsensusSecrets) -> anyhow::Result<Option<node
pub(super) fn executor(
cfg: &ConsensusConfig,
secrets: &ConsensusSecrets,
global_config: &consensus_dal::GlobalConfig,
build_version: Option<semver::Version>,
) -> anyhow::Result<executor::Config> {
let mut gossip_static_outbound = HashMap::new();
// Always connect to seed peers.
// Once we implement dynamic peer discovery,
// we won't establish a persistent connection to seed peers
// but rather just ask them for more peers.
let mut gossip_static_outbound: HashMap<_, _> = global_config
.seed_peers
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
{
let mut append = |key: &NodePublicKey, addr: &Host| {
gossip_static_outbound.insert(
Expand Down
3 changes: 2 additions & 1 deletion core/node/consensus/src/en.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl EN {
));

let executor = executor::Executor {
config: config::executor(&cfg, &secrets, build_version)?,
config: config::executor(&cfg, &secrets, &global_config, build_version)?,
block_store,
batch_store,
validator: config::validator_key(&secrets)
Expand Down Expand Up @@ -304,6 +304,7 @@ impl EN {
Ok(consensus_dal::GlobalConfig {
genesis: zksync_protobuf::serde::deserialize(&genesis.0).context("deserialize()")?,
registry_address: None,
seed_peers: [].into(),
})
}

Expand Down
4 changes: 2 additions & 2 deletions core/node/consensus/src/mn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ pub async fn run_main_node(
s.spawn_bg(run_attestation_controller(
ctx,
&pool,
global_config,
global_config.clone(),
attestation.clone(),
));

let executor = executor::Executor {
config: config::executor(&cfg, &secrets, None)?,
config: config::executor(&cfg, &secrets, &global_config, None)?,
block_store,
batch_store,
validator: Some(executor::Validator {
Expand Down
1 change: 1 addition & 0 deletions core/node/consensus/src/storage/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ impl<'a> Connection<'a> {
}
.with_hash(),
registry_address: spec.registry_address,
seed_peers: spec.seed_peers.clone(),
};

txn.try_update_global_config(ctx, &new)
Expand Down
18 changes: 12 additions & 6 deletions core/node/consensus/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,8 @@ impl ConfigSet {
}
}

pub(super) fn new_configs(
rng: &mut impl Rng,
setup: &Setup,
gossip_peers: usize,
) -> Vec<ConfigSet> {
pub(super) fn new_configs(rng: &mut impl Rng, setup: &Setup, seed_peers: usize) -> Vec<ConfigSet> {
let net_cfgs = network::testonly::new_configs(rng, setup, 0);
let genesis_spec = config::GenesisSpec {
chain_id: setup.genesis.chain_id.0.try_into().unwrap(),
protocol_version: config::ProtocolVersion(setup.genesis.protocol_version.0),
Expand All @@ -117,8 +114,17 @@ pub(super) fn new_configs(
.collect(),
leader: config::ValidatorPublicKey(setup.validator_keys[0].public().encode()),
registry_address: None,
seed_peers: net_cfgs[..seed_peers]
.iter()
.map(|c| {
(
config::NodePublicKey(c.gossip.key.public().encode()),
config::Host(c.public_addr.0.clone()),
)
})
.collect(),
};
network::testonly::new_configs(rng, setup, gossip_peers)
net_cfgs
.into_iter()
.enumerate()
.map(|(i, net)| ConfigSet {
Expand Down
Loading

0 comments on commit e9d1d90

Please sign in to comment.