sn_interface 0.16.20

Safe Network Interfaces. Messaging and Types.
Documentation
// Copyright 2023 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

mod dkg;
mod join;
mod node_msgs;
mod op_id;
mod section_sig;

use super::{data::CmdResponse, MsgId};

use crate::messaging::AuthorityProof;
use crate::network_knowledge::{NodeState, RelocationProof, SapCandidate, SectionTreeUpdate};
use crate::SectionAuthorityProvider;

pub use dkg::DkgSessionId;
pub use join::{JoinRejectReason, JoinRequest, JoinResponse};
pub use node_msgs::{NodeDataCmd, NodeDataQuery, NodeEvent, NodeQueryResponse};
pub use op_id::OperationId;
pub use section_sig::{SectionSig, SectionSigShare, SectionSigned};

use bls::PublicKey as BlsPublicKey;
use ed25519::Signature;
use qp2p::UsrMsgBytes;
use serde::{Deserialize, Serialize};
use sn_consensus::{Generation, SignedVote};
use sn_sdkg::DkgSignedVote;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{self, Display, Formatter};
use xor_name::XorName;

/// List of peers of a section
pub type SectionPeers = BTreeSet<SectionSigned<NodeState>>;

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, custom_debug::Debug)]
pub enum AntiEntropyKind {
    /// This AE message is sent to a peer when a message with outdated section
    /// information was received, attaching the bounced message so
    /// the peer can resend it with up to date destination information.
    Retry {
        #[debug(skip)]
        bounced_msg: UsrMsgBytes,
    },
    /// This AE message is sent to a peer when a message needs to be sent to a
    /// different and/or closest section, attaching the bounced message so the peer
    /// can resend it to the correct section with up to date destination information.
    Redirect {
        #[debug(skip)]
        bounced_msg: UsrMsgBytes,
    },
    /// This AE message is sent to update a peer when we notice they are behind
    Update { members: SectionPeers },
}

/// A vote about the state of the section
/// This can be a result of seeing a node go offline or deciding wether we want to accept new nodes
/// Anything where we need section authority before action can be taken
/// Section State Proposals are sent by elders to elders
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum SectionStateVote {
    /// Vote to remove a node from our section
    NodeIsOffline(NodeState),
    /// Vote to change whether new nodes are allowed to join our section
    JoinsAllowed(bool),
}

#[derive(Clone, PartialEq, Serialize, Deserialize, custom_debug::Debug)]
#[allow(clippy::large_enum_variant, clippy::derive_partial_eq_without_eq)]
/// Message sent over the among nodes
pub enum NodeMsg {
    AntiEntropy {
        /// The update to our NetworkKnowledge containing the current `SectionAuthorityProvider`
        /// and the section chain truncated from the triggering msg's dst section_key or genesis_key
        /// if the the dst section_key is not a direct ancestor to our section_key
        section_tree_update: SectionTreeUpdate,
        /// The kind of anti-entropy response.
        kind: AntiEntropyKind,
    },
    /// Probes the network by sending a message to a random or chosen dst triggering an AE flow.
    /// Sends the current section key of target section which we know
    /// This expects a response, even if we're up to date.
    AntiEntropyProbe(BlsPublicKey),
    /// Send from a section to the node to be immediately relocated.
    Relocate(SectionSigned<NodeState>),
    /// Membership Votes, in order they should be processed in.
    MembershipVotes(Vec<SignedVote<NodeState>>),
    /// Membership Anti-Entropy request
    MembershipAE(Generation),
    /// Try to join a section in the network.
    TryJoin(Option<RelocationProof>),
    /// Response to a join request.
    JoinResponse(JoinResponse),
    /// Sent to the new elder candidates to start the DKG process, along with a sig of the DkgSessionId
    DkgStart(DkgSessionId, SectionSigShare),
    /// Sent when DKG is triggered to other participant
    DkgEphemeralPubKey {
        /// The identifier of the DKG session this message is for.
        session_id: DkgSessionId,
        /// Section authority for the DkgSessionId (if you missed the DkgStart you can trust this)
        section_auth: AuthorityProof<SectionSig>,
        /// The ephemeral bls key chosen by candidate
        pub_key: BlsPublicKey,
        /// The ed25519 signature of the candidate
        sig: Signature,
    },
    /// Votes exchanged for DKG process.
    DkgVotes {
        /// The identifier of the DKG session this message is for.
        session_id: DkgSessionId,
        /// The ephemeral bls public keys used for this Dkg round
        pub_keys: BTreeMap<XorName, (BlsPublicKey, Signature)>,
        /// The DKG message.
        votes: Vec<DkgSignedVote>,
    },
    /// Dkg Anti-Entropy request when receiving votes that are ahead of our knowledge
    DkgAE(DkgSessionId),
    /// After DKG, elder candidates request handover with this to the current elders
    /// by submitting their new SAP along with their sig share
    /// The elders can then aggregate it and confirm the SAP is valid before accepting it for Handover
    RequestHandover {
        /// SAP generated by the candidate's finished DKG session
        sap: SectionAuthorityProvider,
        /// BLS sig share of the candidate over the SAP
        sig_share: SectionSigShare,
    },
    /// Section handover consensus vote message
    HandoverVotes(Vec<SignedVote<SapCandidate>>),
    /// Handover Anti-Entropy request
    HandoverAE(Generation),
    /// After Handover consensus, the elders inform the new elder candidates that they are promoted
    /// The candidates can aggregate the sig_share to obtain SectionSigned proof that they are promoted
    SectionHandoverPromotion {
        /// The promoted SAP (signed by themselves)
        sap: SectionSigned<SectionAuthorityProvider>,
        /// BLS signature share of an Elder over the sap's pubkey
        sig_share: SectionSigShare,
    },
    /// After Handover consensus, the elders inform the new elder candidates that they are promoted
    /// The candidates can aggregate the sig_shares to obtain SectionSigned proof that they are promoted
    SectionSplitPromotion {
        /// The promoted SAP of section 0 (signed by themselves)
        sap0: SectionSigned<SectionAuthorityProvider>,
        /// BLS signature share of an Elder over the sap0's pubkey
        sig_share0: SectionSigShare,
        /// The promoted SAP of section 1 (signed by themselves)
        sap1: SectionSigned<SectionAuthorityProvider>,
        /// BLS signature share of an Elder over the sap1's pubkey
        sig_share1: SectionSigShare,
    },
    /// Elders votes among themselves regarding SectionStates of joins_allowed and voting nodes off
    /// Once fully aggregated in the SectionStateVote aggregator, the proposal is accepted
    ProposeSectionState {
        proposal: SectionStateVote,
        /// BLS signature share of an Elder over the vote
        sig_share: SectionSigShare,
    },
    /// Node events are Node to Elder events about something that happened on a Node.
    NodeEvent(NodeEvent),
    /// Data cmds are orders to perform some data operation, only sent internally in the network.
    NodeDataCmd(NodeDataCmd),
    /// Data queries is a read-only operation.
    NodeDataQuery(NodeDataQuery),
}

impl NodeMsg {
    pub fn is_join(&self) -> bool {
        // we could also differentiate, say if it's a relocation
        matches!(self, NodeMsg::TryJoin(_))
    }
    pub fn is_ae(&self) -> bool {
        matches!(self, NodeMsg::AntiEntropy { .. })
    }
}

/// Messages sent from adults to the elders in response to client queries or commands
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum NodeDataResponse {
    /// The response to a query, containing the query result.
    QueryResponse {
        /// The result of the query.
        response: NodeQueryResponse,
        /// ID of the requested operation.
        operation_id: OperationId,
    },
    /// The response will be sent back to the client when the handling on the
    /// receiving Elder has been finished.
    CmdResponse {
        /// The result of the command.
        response: CmdResponse,
        /// ID of causing ClientMsg::Cmd.
        correlation_id: MsgId,
    },
}

impl Display for NodeDataResponse {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::QueryResponse { response, .. } => {
                write!(f, "NodeDataResponse::QueryResponse({response:?})")
            }
            Self::CmdResponse { response, .. } => {
                write!(f, "NodeDataResponse::CmdResponse({response:?})")
            }
        }
    }
}

impl NodeMsg {
    pub fn statemap_states(&self) -> crate::statemap::State {
        use crate::statemap::State;
        match self {
            Self::AntiEntropy { .. } => State::AntiEntropy,
            Self::AntiEntropyProbe { .. } => State::AntiEntropy,
            Self::Relocate(_) => State::Relocate,
            Self::MembershipAE(_) => State::Membership,
            Self::MembershipVotes(_) => State::Membership,
            Self::TryJoin(_) => State::Join,
            Self::JoinResponse(_) => State::Join,
            Self::DkgStart { .. } => State::Dkg,
            Self::DkgEphemeralPubKey { .. } => State::Dkg,
            Self::DkgVotes { .. } => State::Dkg,
            Self::DkgAE { .. } => State::Dkg,
            Self::RequestHandover { .. } => State::Dkg,
            Self::HandoverVotes(_) => State::Handover,
            Self::HandoverAE(_) => State::Handover,
            Self::SectionHandoverPromotion { .. } => State::Handover,
            Self::SectionSplitPromotion { .. } => State::Handover,
            Self::ProposeSectionState { .. } => State::Propose,
            Self::NodeEvent(_) => State::Node,
            Self::NodeDataCmd(_) => State::Node,
            Self::NodeDataQuery(_) => State::Node,
        }
    }
}

impl Display for NodeMsg {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::AntiEntropy { .. } => write!(f, "NodeMsg::AntiEntropy"),
            Self::AntiEntropyProbe { .. } => write!(f, "NodeMsg::AntiEntropyProbe"),
            Self::Relocate { .. } => write!(f, "NodeMsg::Relocate"),
            Self::MembershipVotes { .. } => write!(f, "NodeMsg::MembershipVotes"),
            Self::MembershipAE { .. } => write!(f, "NodeMsg::MembershipAE"),
            Self::TryJoin(_) => write!(f, "NodeMsg::TryJoin"),
            Self::JoinResponse { .. } => write!(f, "NodeMsg::JoinResponse"),
            Self::DkgStart { .. } => write!(f, "NodeMsg::DkgStart"),
            Self::DkgEphemeralPubKey { .. } => write!(f, "NodeMsg::DkgEphemeralPubKey"),
            Self::DkgVotes { .. } => write!(f, "NodeMsg::DkgVotes"),
            Self::DkgAE { .. } => write!(f, "NodeMsg::DkgAE"),
            Self::RequestHandover { .. } => write!(f, "NodeMsg::RequestHandover"),
            Self::HandoverVotes { .. } => write!(f, "NodeMsg::HandoverVotes"),
            Self::HandoverAE { .. } => write!(f, "NodeMsg::HandoverAE"),
            Self::SectionHandoverPromotion { .. } => write!(f, "NodeMsg::SectionHandoverPromotion"),
            Self::SectionSplitPromotion { .. } => write!(f, "NodeMsg::SectionSplitPromotion"),
            Self::ProposeSectionState { .. } => write!(f, "NodeMsg::ProposeSectionState"),
            Self::NodeEvent { .. } => write!(f, "NodeMsg::NodeEvent"),
            Self::NodeDataCmd { .. } => write!(f, "NodeMsg::NodeCmd"),
            Self::NodeDataQuery { .. } => write!(f, "NodeMsg::NodeQuery"),
        }
    }
}