use super::NodeState;
use crate::{
messaging::system::{DkgSessionId, SectionSig, SectionSigned},
network_knowledge::SectionsDAG,
types::Peer,
};
use bls::{PublicKey, PublicKeySet};
use serde::{Deserialize, Serialize};
use sn_consensus::Generation;
use std::{
collections::BTreeSet,
fmt::{self, Debug, Display, Formatter},
net::SocketAddr,
};
use xor_name::{Prefix, XorName};
pub trait SectionAuthUtils<T: Serialize> {
fn new(value: T, sig: SectionSig) -> Self;
fn verify(&self, section_dag: &SectionsDAG) -> bool;
fn self_verify(&self) -> bool;
}
impl<T: Serialize> SectionAuthUtils<T> for SectionSigned<T> {
fn new(value: T, sig: SectionSig) -> Self {
Self { value, sig }
}
fn verify(&self, section_dag: &SectionsDAG) -> bool {
section_dag.has_key(&self.sig.public_key) && self.self_verify()
}
fn self_verify(&self) -> bool {
bincode::serialize(&self.value)
.map(|bytes| self.sig.verify(&bytes))
.unwrap_or(false)
}
}
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct SectionAuthorityProvider {
prefix: Prefix,
public_key_set: PublicKeySet,
elders: BTreeSet<Peer>,
members: BTreeSet<NodeState>,
membership_gen: Generation,
}
#[allow(clippy::large_enum_variant)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize)]
pub enum SapCandidate {
ElderHandover(SectionSigned<SectionAuthorityProvider>),
SectionSplit(
SectionSigned<SectionAuthorityProvider>,
SectionSigned<SectionAuthorityProvider>,
),
}
impl SapCandidate {
pub fn elders(&self) -> Vec<Peer> {
match self {
SapCandidate::ElderHandover(sap) => sap.elders_vec(),
SapCandidate::SectionSplit(sap1, sap2) => {
[sap1.elders_vec(), sap2.elders_vec()].concat().to_vec()
}
}
}
}
impl Debug for SectionAuthorityProvider {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
#[derive(Debug)]
enum PeerStatus {
Elder,
Member,
}
let elders: BTreeSet<_> = self.elders.iter().map(|peer| peer.name()).collect();
let mut elder_count = 0;
let mut peers: Vec<_> = self
.members()
.map(|peer| {
let status = if elders.contains(&peer.name()) {
elder_count += 1;
PeerStatus::Elder
} else {
PeerStatus::Member
};
(peer, status)
})
.collect();
peers.sort_by_key(|(_, is_elder)| !matches!(is_elder, PeerStatus::Elder));
let mut f = f.debug_struct(format!("SAP {:?}", self.prefix).as_str());
let f = f
.field("elders", &elders.len())
.field("members", &self.members.len())
.field("gen", &self.membership_gen);
if elder_count != elders.len() {
f.field(
"elders (error: some elders are not part of members)",
&elders,
)
.field("members", &self.members().collect::<Vec<_>>())
.finish()
} else {
f.field("peers", &peers).finish()
}
}
}
impl Display for SectionAuthorityProvider {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self, f)
}
}
impl SectionAuthorityProvider {
pub fn new<E, M>(
elders: E,
prefix: Prefix,
members: M,
pk_set: PublicKeySet,
membership_gen: Generation,
) -> Self
where
E: IntoIterator<Item = Peer>,
M: IntoIterator<Item = NodeState>,
{
Self {
prefix,
public_key_set: pk_set,
elders: elders.into_iter().collect(),
members: members.into_iter().collect(),
membership_gen,
}
}
pub fn from_dkg_session(session_id: &DkgSessionId, pk_set: PublicKeySet) -> Self {
Self::new(
session_id.elder_peers(),
session_id.prefix,
session_id.bootstrap_members.clone(),
pk_set,
session_id.membership_gen,
)
}
pub fn prefix(&self) -> Prefix {
self.prefix
}
pub fn elders(&self) -> impl Iterator<Item = &Peer> + '_ {
self.elders.iter()
}
pub fn members(&self) -> impl Iterator<Item = &NodeState> + '_ {
self.members.iter()
}
pub fn membership_gen(&self) -> Generation {
self.membership_gen
}
pub fn elders_vec(&self) -> Vec<Peer> {
self.elders.iter().cloned().collect()
}
pub fn elders_set(&self) -> BTreeSet<Peer> {
self.elders.iter().cloned().collect()
}
pub fn public_key_set(&self) -> PublicKeySet {
self.public_key_set.clone()
}
pub fn elder_count(&self) -> usize {
self.elders.len()
}
pub fn contains_elder(&self, name: &XorName) -> bool {
self.elders.iter().any(|elder| &elder.name() == name)
}
pub fn get_elder(&self, name: &XorName) -> Option<&Peer> {
self.elders.iter().find(|elder| elder.name() == *name)
}
pub fn names(&self) -> BTreeSet<XorName> {
self.elders.iter().map(Peer::name).collect()
}
pub fn addresses(&self) -> Vec<SocketAddr> {
self.elders.iter().map(Peer::addr).collect()
}
pub fn section_key(&self) -> PublicKey {
self.public_key_set.public_key()
}
}
#[cfg(any(test, feature = "test-utils"))]
pub mod test_utils {
use crate::{
elder_count,
network_knowledge::{supermajority, MyNodeInfo, NodeState, SectionAuthorityProvider},
test_utils::gen_sorted_nodes,
};
use rand::{thread_rng, RngCore};
use xor_name::Prefix;
pub fn gen_sk_set(
mut rng: impl RngCore,
elder_count: usize,
sk_threshold_size: Option<usize>,
) -> bls::SecretKeySet {
bls::SecretKeySet::random(
sk_threshold_size.unwrap_or_else(|| supermajority(elder_count).saturating_sub(1)),
&mut rng,
)
}
pub struct TestSapBuilder {
prefix: Prefix,
elder_count: usize,
adult_count: usize,
membership_gen: usize,
elder_nodes: Option<Vec<MyNodeInfo>>,
adult_nodes: Option<Vec<MyNodeInfo>>,
sk_set: Option<bls::SecretKeySet>,
sk_threshold_size: Option<usize>,
elder_age_pattern: Option<Vec<u8>>,
}
impl TestSapBuilder {
pub fn new(prefix: Prefix) -> Self {
Self {
prefix,
elder_count: elder_count(),
adult_count: 0,
membership_gen: 0,
sk_threshold_size: None,
elder_nodes: None,
adult_nodes: None,
sk_set: None,
elder_age_pattern: None,
}
}
pub fn elder_count(mut self, elder_count: usize) -> Self {
self.elder_count = elder_count;
self
}
pub fn adult_count(mut self, adult_count: usize) -> Self {
self.adult_count = adult_count;
self
}
pub fn membership_gen(mut self, gen: usize) -> Self {
self.membership_gen = gen;
self
}
pub fn elder_nodes(mut self, nodes: Vec<MyNodeInfo>) -> Self {
self.elder_count = nodes.len();
self.elder_nodes = Some(nodes);
self
}
pub fn adult_nodes(mut self, nodes: Vec<MyNodeInfo>) -> Self {
self.adult_count = nodes.len();
self.adult_nodes = Some(nodes);
self
}
pub fn sk_set(mut self, sk_set: &bls::SecretKeySet) -> Self {
self.sk_set = Some(sk_set.clone());
self
}
pub fn sk_threshold_size(mut self, sk_threshold_size: usize) -> Self {
self.sk_threshold_size = Some(sk_threshold_size);
self
}
pub fn elder_age_pattern(mut self, pattern: Vec<u8>) -> Self {
self.elder_age_pattern = Some(pattern);
self
}
pub fn build_rng(
self,
rng: impl RngCore,
) -> (
SectionAuthorityProvider,
bls::SecretKeySet,
Vec<MyNodeInfo>,
Vec<MyNodeInfo>,
) {
let members = gen_sorted_nodes(
&self.prefix,
self.elder_count,
self.adult_count,
self.elder_age_pattern.as_deref(),
);
let elder_nodes = if let Some(elders) = self.elder_nodes {
elders
} else {
members.iter().take(self.elder_count).cloned().collect()
};
let adult_nodes = if let Some(adults) = self.adult_nodes {
adults
} else {
members.iter().skip(self.elder_count).cloned().collect()
};
let members = elder_nodes
.iter()
.chain(adult_nodes.iter())
.map(|i| NodeState::joined(i.peer(), None));
let sk_set = if let Some(sk) = self.sk_set {
sk
} else {
gen_sk_set(rng, self.elder_count, self.sk_threshold_size)
};
let sap = SectionAuthorityProvider::new(
elder_nodes.iter().map(|i| i.peer()),
self.prefix,
members,
sk_set.public_keys(),
self.membership_gen as u64,
);
(sap, sk_set, elder_nodes, adult_nodes)
}
pub fn build(
self,
) -> (
SectionAuthorityProvider,
bls::SecretKeySet,
Vec<MyNodeInfo>,
Vec<MyNodeInfo>,
) {
self.build_rng(thread_rng())
}
}
}