use super::NodeState;
use crate::messaging::system::SectionSigned;
use crate::network_knowledge::{Error, Result};
use crate::types::utils::calc_age;
use ed25519_dalek::{PublicKey, Signature, Verifier};
use hex_fmt::HexFmt;
use serde::{Deserialize, Serialize};
use sn_consensus::Decision;
use xor_name::XorName;
use std::fmt::{self, Display, Formatter};
pub struct ChurnId(pub XorName);
impl Display for ChurnId {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(
fmt,
"Churn-{:02x}{:02x}{:02x}..",
self.0[0], self.0[1], self.0[2]
)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, Debug)]
pub struct RelocationTrigger(Decision<NodeState>);
impl RelocationTrigger {
pub fn new(decision: Decision<NodeState>) -> Self {
Self(decision)
}
pub fn dst_section(&self, peer_name: XorName) -> XorName {
let mut content_parts = Vec::new();
content_parts.push(peer_name.0.to_vec());
for sig in self.0.proposals.values() {
content_parts.push(sig.to_bytes().to_vec());
}
XorName::from_content_parts(
Vec::from_iter(content_parts.iter().map(|v| v.as_slice())).as_slice(),
)
}
pub fn churn_id(&self) -> ChurnId {
let mut content_parts = Vec::new();
for sig in self.0.proposals.values() {
content_parts.push(sig.to_bytes().to_vec());
}
ChurnId(XorName::from_content_parts(
Vec::from_iter(content_parts.iter().map(|v| v.as_slice())).as_slice(),
))
}
}
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
pub struct RelocationInfo {
signed_relocation: SectionSigned<NodeState>,
new_name: XorName,
}
#[derive(Clone, PartialEq, Serialize, Deserialize, custom_debug::Debug)]
pub struct RelocationProof {
info: RelocationInfo,
#[serde(with = "serde_bytes")]
#[debug(with = "Self::fmt_ed25519")]
self_sig: Signature,
self_old_key: PublicKey,
}
impl RelocationInfo {
pub fn new(signed_relocation: SectionSigned<NodeState>, new_name: XorName) -> Self {
Self {
signed_relocation,
new_name,
}
}
}
impl RelocationProof {
pub fn new(info: RelocationInfo, self_sig: Signature, self_old_key: PublicKey) -> Self {
Self {
info,
self_sig,
self_old_key,
}
}
pub fn signed_by(&self) -> &bls::PublicKey {
&self.info.signed_relocation.sig.public_key
}
pub fn verify(&self) -> Result<()> {
if self.old_key_name() != self.info.signed_relocation.name() {
return Err(Error::InvalidRelocationProof);
}
let serialized_info =
bincode::serialize(&self.info).map_err(|_err| Error::InvalidRelocationProof)?;
self.self_old_key
.verify(&serialized_info, &self.self_sig)
.map_err(|_err| Error::InvalidRelocationProof)?;
let serialized_state = bincode::serialize(&self.info.signed_relocation.value)
.map_err(|_err| Error::InvalidRelocationProof)?;
if !self.info.signed_relocation.sig.verify(&serialized_state) {
Err(Error::InvalidRelocationProof)
} else {
Ok(())
}
}
pub fn new_name(&self) -> XorName {
self.info.new_name
}
pub fn new_age(&self) -> u8 {
calc_age(&self.new_name())
}
pub fn previous_name(&self) -> XorName {
self.info.signed_relocation.name()
}
pub fn previous_age(&self) -> u8 {
self.info.signed_relocation.age()
}
pub fn signed_relocation(&self) -> &SectionSigned<NodeState> {
&self.info.signed_relocation
}
fn fmt_ed25519(sig: &Signature, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Signature({:0.10})", HexFmt(sig))
}
fn old_key_name(&self) -> XorName {
use crate::types::PublicKey::Ed25519;
XorName::from(Ed25519(self.self_old_key))
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
pub enum RelocationState {
NoRelocation,
PreparingToRelocate(RelocationTrigger),
ReadyToJoinNewSection(RelocationProof),
}
impl RelocationState {
pub fn proof(&self) -> Option<&RelocationProof> {
match self {
Self::ReadyToJoinNewSection(proof) => Some(proof),
_ => None,
}
}
}