diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 4d37fab7..e1636482 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -177,6 +177,8 @@ jobs: cargo msrv verify --manifest-path coordinator/tributary/Cargo.toml cargo msrv verify --manifest-path coordinator/cosign/Cargo.toml cargo msrv verify --manifest-path coordinator/substrate/Cargo.toml + cargo msrv verify --manifest-path coordinator/p2p/Cargo.toml + cargo msrv verify --manifest-path coordinator/p2p/libp2p/Cargo.toml cargo msrv verify --manifest-path coordinator/Cargo.toml msrv-substrate: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 65a35cc3..0c311b99 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,6 +63,8 @@ jobs: -p tributary-chain \ -p serai-cosign \ -p serai-coordinator-substrate \ + -p serai-coordinator-p2p \ + -p serai-coordinator-libp2p-p2p \ -p serai-coordinator \ -p serai-orchestrator \ -p serai-docker-tests diff --git a/Cargo.lock b/Cargo.lock index 49bbf65d..dfeea6f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8311,7 +8311,6 @@ dependencies = [ name = "serai-coordinator" version = "0.1.0" dependencies = [ - "async-trait", "bitvec", "blake2", "borsh", @@ -8319,9 +8318,7 @@ dependencies = [ "env_logger", "flexible-transcript", "frost-schnorrkel", - "futures-util", "hex", - "libp2p", "log", "modular-frost", "parity-scale-codec", @@ -8329,6 +8326,9 @@ dependencies = [ "schnorr-signatures", "schnorrkel", "serai-client", + "serai-coordinator-libp2p-p2p", + "serai-coordinator-p2p", + "serai-coordinator-substrate", "serai-cosign", "serai-db", "serai-env", @@ -8337,13 +8337,49 @@ dependencies = [ "serai-task", "sp-application-crypto", "sp-runtime", + "tributary-chain", + "zalloc", + "zeroize", +] + +[[package]] +name = "serai-coordinator-libp2p-p2p" +version = "0.1.0" +dependencies = [ + "async-trait", + "blake2", + "borsh", + "futures-util", + "hex", + "libp2p", + "log", + "rand_core", + "schnorrkel", + "serai-client", + "serai-coordinator-p2p", + "serai-cosign", + "serai-task", "tokio", "tributary-chain", "void", - "zalloc", "zeroize", ] +[[package]] +name = "serai-coordinator-p2p" +version = "0.1.0" +dependencies = [ + "borsh", + "futures-util", + "log", + "serai-client", + "serai-cosign", + "serai-db", + "serai-task", + "tokio", + "tributary-chain", +] + [[package]] name = "serai-coordinator-substrate" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 688537b1..39507b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,8 @@ members = [ "coordinator/tributary", "coordinator/cosign", "coordinator/substrate", + "coordinator/p2p", + "coordinator/p2p/libp2p", "coordinator", "substrate/primitives", diff --git a/coordinator/Cargo.toml b/coordinator/Cargo.toml index e0b84346..3ecce4be 100644 --- a/coordinator/Cargo.toml +++ b/coordinator/Cargo.toml @@ -18,8 +18,6 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] -async-trait = { version = "0.1", default-features = false } - zeroize = { version = "^1.5", default-features = false, features = ["std"] } bitvec = { version = "1", default-features = false, features = ["std"] } rand_core = { version = "0.6", default-features = false, features = ["std"] } @@ -53,12 +51,10 @@ borsh = { version = "1", default-features = false, features = ["std", "derive", log = { version = "0.4", default-features = false, features = ["std"] } env_logger = { version = "0.10", default-features = false, features = ["humantime"] } -futures-util = { version = "0.3", default-features = false, features = ["std"] } -tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "sync", "time", "macros"] } -void = { version = "1", default-features = false } -libp2p = { version = "0.52", default-features = false, features = ["tokio", "tcp", "noise", "yamux", "ping", "request-response", "gossipsub", "macros"] } - serai-cosign = { path = "./cosign" } +serai-coordinator-substrate = { path = "./substrate" } +serai-coordinator-p2p = { path = "./p2p" } +serai-coordinator-libp2p-p2p = { path = "./p2p/libp2p" } [dev-dependencies] tributary = { package = "tributary-chain", path = "./tributary", features = ["tests"] } diff --git a/coordinator/LICENSE b/coordinator/LICENSE index 26d57cbb..621233a9 100644 --- a/coordinator/LICENSE +++ b/coordinator/LICENSE @@ -1,6 +1,6 @@ AGPL-3.0-only license -Copyright (c) 2023-2024 Luke Parker +Copyright (c) 2023-2025 Luke Parker This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License Version 3 as diff --git a/coordinator/p2p/Cargo.toml b/coordinator/p2p/Cargo.toml new file mode 100644 index 00000000..44183258 --- /dev/null +++ b/coordinator/p2p/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "serai-coordinator-p2p" +version = "0.1.0" +description = "Serai coordinator's P2P abstraction" +license = "AGPL-3.0-only" +repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/p2p" +authors = ["Luke Parker "] +keywords = [] +edition = "2021" +publish = false +rust-version = "1.81" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } + +serai-db = { path = "../../common/db", version = "0.1" } + +serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] } +serai-cosign = { path = "../cosign" } +tributary = { package = "tributary-chain", path = "../tributary" } + +futures-util = { version = "0.3", default-features = false, features = ["std"] } +tokio = { version = "1", default-features = false, features = ["sync"] } + +log = { version = "0.4", default-features = false, features = ["std"] } +serai-task = { path = "../../common/task", version = "0.1" } diff --git a/coordinator/p2p/LICENSE b/coordinator/p2p/LICENSE new file mode 100644 index 00000000..621233a9 --- /dev/null +++ b/coordinator/p2p/LICENSE @@ -0,0 +1,15 @@ +AGPL-3.0-only license + +Copyright (c) 2023-2025 Luke Parker + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/coordinator/p2p/README.md b/coordinator/p2p/README.md new file mode 100644 index 00000000..9d6d9aef --- /dev/null +++ b/coordinator/p2p/README.md @@ -0,0 +1,3 @@ +# Serai Coordinator P2P + +The P2P abstraction used by Serai's coordinator. diff --git a/coordinator/p2p/libp2p/Cargo.toml b/coordinator/p2p/libp2p/Cargo.toml new file mode 100644 index 00000000..8916d961 --- /dev/null +++ b/coordinator/p2p/libp2p/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "serai-coordinator-libp2p-p2p" +version = "0.1.0" +description = "Serai coordinator's libp2p-based P2P backend" +license = "AGPL-3.0-only" +repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/p2p/libp2p" +authors = ["Luke Parker "] +keywords = [] +edition = "2021" +publish = false +rust-version = "1.81" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +async-trait = { version = "0.1", default-features = false } + +rand_core = { version = "0.6", default-features = false, features = ["std"] } + +zeroize = { version = "^1.5", default-features = false, features = ["std"] } +blake2 = { version = "0.10", default-features = false, features = ["std"] } +schnorrkel = { version = "0.11", default-features = false, features = ["std"] } + +hex = { version = "0.4", default-features = false, features = ["std"] } +borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } + +serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai", "borsh"] } +serai-cosign = { path = "../../cosign" } +tributary = { package = "tributary-chain", path = "../../tributary" } + +void = { version = "1", default-features = false } +futures-util = { version = "0.3", default-features = false, features = ["std"] } +tokio = { version = "1", default-features = false, features = ["sync"] } +libp2p = { version = "0.52", default-features = false, features = ["tokio", "tcp", "noise", "yamux", "ping", "request-response", "gossipsub", "macros"] } + +log = { version = "0.4", default-features = false, features = ["std"] } +serai-task = { path = "../../../common/task", version = "0.1" } +serai-coordinator-p2p = { path = "../" } diff --git a/coordinator/p2p/libp2p/LICENSE b/coordinator/p2p/libp2p/LICENSE new file mode 100644 index 00000000..621233a9 --- /dev/null +++ b/coordinator/p2p/libp2p/LICENSE @@ -0,0 +1,15 @@ +AGPL-3.0-only license + +Copyright (c) 2023-2025 Luke Parker + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/coordinator/p2p/libp2p/README.md b/coordinator/p2p/libp2p/README.md new file mode 100644 index 00000000..82edec80 --- /dev/null +++ b/coordinator/p2p/libp2p/README.md @@ -0,0 +1,14 @@ +# Serai Coordinator libp2p P2P + +A libp2p-backed P2P instantiation for Serai's coordinator. + +The libp2p swarm is limited to validators from the Serai network. The swarm +does not maintain any of its own peer finding/routing infrastructure, instead +relying on the Serai network's connection information to dial peers. This does +limit the listening peers to only the peers immediately reachable via the same +IP address (despite the two distinct services), not hidden behind a NAT, yet is +also quite simple and gives full control of who to connect to to us. + +Peers are decided via the internal `DialTask` which aims to maintain a target +amount of peers for each external network. This ensures cosigns are able to +propagate across the external networks which sign them. diff --git a/coordinator/src/p2p/libp2p/authenticate.rs b/coordinator/p2p/libp2p/src/authenticate.rs similarity index 99% rename from coordinator/src/p2p/libp2p/authenticate.rs rename to coordinator/p2p/libp2p/src/authenticate.rs index 56e5336b..fbdcf7c9 100644 --- a/coordinator/src/p2p/libp2p/authenticate.rs +++ b/coordinator/p2p/libp2p/src/authenticate.rs @@ -17,7 +17,7 @@ use libp2p::{ noise, }; -use crate::p2p::libp2p::peer_id_from_public; +use crate::peer_id_from_public; const PROTOCOL: &str = "/serai/coordinator/validators"; diff --git a/coordinator/src/p2p/libp2p/dial.rs b/coordinator/p2p/libp2p/src/dial.rs similarity index 98% rename from coordinator/src/p2p/libp2p/dial.rs rename to coordinator/p2p/libp2p/src/dial.rs index e8611797..f8576217 100644 --- a/coordinator/src/p2p/libp2p/dial.rs +++ b/coordinator/p2p/libp2p/src/dial.rs @@ -14,7 +14,7 @@ use libp2p::{ use serai_task::ContinuallyRan; -use crate::p2p::libp2p::{PORT, Peers, validators::Validators}; +use crate::{PORT, Peers, validators::Validators}; const TARGET_PEERS_PER_NETWORK: usize = 5; /* diff --git a/coordinator/src/p2p/libp2p/gossip.rs b/coordinator/p2p/libp2p/src/gossip.rs similarity index 97% rename from coordinator/src/p2p/libp2p/gossip.rs rename to coordinator/p2p/libp2p/src/gossip.rs index f64fddb5..f48c1c4e 100644 --- a/coordinator/src/p2p/libp2p/gossip.rs +++ b/coordinator/p2p/libp2p/src/gossip.rs @@ -15,8 +15,6 @@ use serai_cosign::SignedCosign; // Block size limit + 16 KB of space for signatures/metadata pub(crate) const MAX_LIBP2P_GOSSIP_MESSAGE_SIZE: usize = tributary::BLOCK_SIZE_LIMIT + 16384; -const KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(80); - const LIBP2P_PROTOCOL: &str = "/serai/coordinator/gossip/1.0.0"; const BASE_TOPIC: &str = "/"; diff --git a/coordinator/src/p2p/libp2p/mod.rs b/coordinator/p2p/libp2p/src/lib.rs similarity index 91% rename from coordinator/src/p2p/libp2p/mod.rs rename to coordinator/p2p/libp2p/src/lib.rs index 3e799ab7..0778813f 100644 --- a/coordinator/src/p2p/libp2p/mod.rs +++ b/coordinator/p2p/libp2p/src/lib.rs @@ -1,14 +1,6 @@ -//! A libp2p-based backend for P2p. -//! -//! The libp2p swarm is limited to validators from the Serai network. The swarm does not maintain -//! any of its own peer finding/routing infrastructure, instead relying on the Serai network's -//! connection information to dial peers. This does limit the listening peers to only the peers -//! immediately reachable via the same IP address (despite the two distinct services), not hidden -//! behind a NAT, yet is also quite simple and gives full control of who to connect to to us. -//! -//! Peers are decided via the `DialTask` which aims to maintain a target amount of peers from each -//! external network. -// TODO: Consider adding that infrastructure, leaving the Serai network solely for bootstrapping +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] use core::{future::Future, time::Duration}; use std::{ @@ -43,7 +35,7 @@ use libp2p::{ SwarmBuilder, }; -use crate::p2p::TributaryBlockWithCommit; +use serai_coordinator_p2p::TributaryBlockWithCommit; /// A struct to sync the validators from the Serai node in order to keep track of them. mod validators; @@ -88,11 +80,12 @@ fn peer_id_from_public(public: PublicKey) -> PeerId { PeerId::from_multihash(Multihash::wrap(0, &public.0).unwrap()).unwrap() } -struct Peer<'a> { +/// The representation of a peer. +pub struct Peer<'a> { outbound_requests: &'a mpsc::UnboundedSender<(PeerId, Request, oneshot::Sender)>, id: PeerId, } -impl crate::p2p::Peer<'_> for Peer<'_> { +impl serai_coordinator_p2p::Peer<'_> for Peer<'_> { fn send_heartbeat( &self, set: ValidatorSet, @@ -123,6 +116,8 @@ struct Peers { peers: Arc>>>, } +// Consider adding identify/kad/autonat/rendevous/(relay + dcutr). While we currently use the Serai +// network for peers, we could use it solely for bootstrapping/as a fallback. #[derive(NetworkBehaviour)] struct Behavior { // Used to only allow Serai validators as peers @@ -137,8 +132,13 @@ struct Behavior { gossip: gossip::Behavior, } +/// The libp2p-backed P2P implementation. +/// +/// The P2p trait implementation does not support backpressure and is expected to be fully +/// utilized. Failure to poll the entire API will cause unbounded memory growth. +#[allow(clippy::type_complexity)] #[derive(Clone)] -struct Libp2p { +pub struct Libp2p { peers: Peers, gossip: mpsc::UnboundedSender, @@ -155,7 +155,10 @@ struct Libp2p { } impl Libp2p { - pub(crate) fn new(serai_key: &Zeroizing, serai: Serai) -> Libp2p { + /// Create a new libp2p-backed P2P instance. + /// + /// This will spawn all of the internal tasks necessary for functioning. + pub fn new(serai_key: &Zeroizing, serai: Serai) -> Libp2p { // Define the object we track peers with let peers = Peers { peers: Arc::new(RwLock::new(HashMap::new())) }; @@ -320,7 +323,7 @@ impl serai_cosign::RequestNotableCosigns for Libp2p { } } -impl crate::p2p::P2p for Libp2p { +impl serai_coordinator_p2p::P2p for Libp2p { type Peer<'a> = Peer<'a>; fn peers(&self, network: NetworkId) -> impl Send + Future>> { @@ -353,6 +356,9 @@ impl crate::p2p::P2p for Libp2p { tokio::spawn({ let respond = self.inbound_request_responses.clone(); async move { + // The swarm task expects us to respond to every request. If the caller drops this + // channel, we'll receive `Err` and respond with `None`, safely satisfying that bound + // without requiring the caller send a value down this channel let response = if let Ok(blocks) = receiver.await { Response::Blocks(blocks) } else { Response::None }; respond diff --git a/coordinator/src/p2p/libp2p/ping.rs b/coordinator/p2p/libp2p/src/ping.rs similarity index 100% rename from coordinator/src/p2p/libp2p/ping.rs rename to coordinator/p2p/libp2p/src/ping.rs diff --git a/coordinator/src/p2p/libp2p/reqres.rs b/coordinator/p2p/libp2p/src/reqres.rs similarity index 95% rename from coordinator/src/p2p/libp2p/reqres.rs rename to coordinator/p2p/libp2p/src/reqres.rs index 8fe02c30..4f8fa236 100644 --- a/coordinator/src/p2p/libp2p/reqres.rs +++ b/coordinator/p2p/libp2p/src/reqres.rs @@ -15,12 +15,12 @@ pub use request_response::{RequestId, Message}; use serai_cosign::SignedCosign; -use crate::p2p::TributaryBlockWithCommit; +use serai_coordinator_p2p::TributaryBlockWithCommit; /// The maximum message size for the request-response protocol // This is derived from the heartbeat message size as it's our largest message pub(crate) const MAX_LIBP2P_REQRES_MESSAGE_SIZE: usize = - (tributary::BLOCK_SIZE_LIMIT * crate::p2p::heartbeat::BLOCKS_PER_BATCH) + 1024; + (tributary::BLOCK_SIZE_LIMIT * serai_coordinator_p2p::heartbeat::BLOCKS_PER_BATCH) + 1024; const PROTOCOL: &str = "/serai/coordinator/reqres/1.0.0"; @@ -103,7 +103,7 @@ impl CodecTrait for Codec { } async fn read_response( &mut self, - proto: &Self::Protocol, + _: &Self::Protocol, io: &mut R, ) -> io::Result { Self::read(io).await @@ -118,7 +118,7 @@ impl CodecTrait for Codec { } async fn write_response( &mut self, - proto: &Self::Protocol, + _: &Self::Protocol, io: &mut W, res: Response, ) -> io::Result<()> { diff --git a/coordinator/src/p2p/libp2p/swarm.rs b/coordinator/p2p/libp2p/src/swarm.rs similarity index 99% rename from coordinator/src/p2p/libp2p/swarm.rs rename to coordinator/p2p/libp2p/src/swarm.rs index f62cb659..e0a6762b 100644 --- a/coordinator/src/p2p/libp2p/swarm.rs +++ b/coordinator/p2p/libp2p/src/swarm.rs @@ -21,7 +21,7 @@ use libp2p::{ swarm::{dial_opts::DialOpts, SwarmEvent, Swarm}, }; -use crate::p2p::libp2p::{ +use crate::{ Peers, BehaviorEvent, Behavior, validators::{self, Validators}, ping, diff --git a/coordinator/src/p2p/libp2p/validators.rs b/coordinator/p2p/libp2p/src/validators.rs similarity index 97% rename from coordinator/src/p2p/libp2p/validators.rs rename to coordinator/p2p/libp2p/src/validators.rs index 7ba48907..0ce4c91b 100644 --- a/coordinator/src/p2p/libp2p/validators.rs +++ b/coordinator/p2p/libp2p/src/validators.rs @@ -13,7 +13,7 @@ use libp2p::PeerId; use futures_util::stream::{StreamExt, FuturesUnordered}; use tokio::sync::{mpsc, RwLock}; -use crate::p2p::libp2p::peer_id_from_public; +use crate::peer_id_from_public; pub(crate) struct Changes { pub(crate) removed: HashSet, @@ -157,10 +157,6 @@ impl Validators { &self.by_network } - pub(crate) fn contains(&self, peer_id: &PeerId) -> bool { - self.validators.contains_key(peer_id) - } - pub(crate) fn networks(&self, peer_id: &PeerId) -> Option<&HashSet> { self.validators.get(peer_id) } diff --git a/coordinator/src/p2p/heartbeat.rs b/coordinator/p2p/src/heartbeat.rs similarity index 91% rename from coordinator/src/p2p/heartbeat.rs rename to coordinator/p2p/src/heartbeat.rs index 025bfd73..87827e7f 100644 --- a/coordinator/src/p2p/heartbeat.rs +++ b/coordinator/p2p/src/heartbeat.rs @@ -5,20 +5,17 @@ use serai_client::validator_sets::primitives::ValidatorSet; use futures_util::FutureExt; -use tributary::{ReadWrite, Block, Tributary, TributaryReader}; +use tributary::{ReadWrite, TransactionTrait, Block, Tributary, TributaryReader}; use serai_db::*; use serai_task::ContinuallyRan; -use crate::{ - tributary::Transaction, - p2p::{Peer, P2p}, -}; +use crate::{Peer, P2p}; // Amount of blocks in a minute const BLOCKS_PER_MINUTE: usize = (60 / (tributary::tendermint::TARGET_BLOCK_TIME / 1000)) as usize; -// Maximum amount of blocks to send in a batch of blocks +/// The maximum amount of blocks to include/included within a batch. pub const BLOCKS_PER_BATCH: usize = BLOCKS_PER_MINUTE + 1; /// Sends a heartbeat to other validators on regular intervals informing them of our Tributary's @@ -26,14 +23,14 @@ pub const BLOCKS_PER_BATCH: usize = BLOCKS_PER_MINUTE + 1; /// /// If the other validator has more blocks then we do, they're expected to inform us. This forms /// the sync protocol for our Tributaries. -struct HeartbeatTask { +pub struct HeartbeatTask { set: ValidatorSet, - tributary: Tributary, - reader: TributaryReader, + tributary: Tributary, + reader: TributaryReader, p2p: P, } -impl ContinuallyRan for HeartbeatTask { +impl ContinuallyRan for HeartbeatTask { fn run_iteration(&mut self) -> impl Send + Future> { async move { // If our blockchain hasn't had a block in the past minute, trigger the heartbeat protocol diff --git a/coordinator/src/p2p/mod.rs b/coordinator/p2p/src/lib.rs similarity index 76% rename from coordinator/src/p2p/mod.rs rename to coordinator/p2p/src/lib.rs index 4bb657f4..26529fef 100644 --- a/coordinator/src/p2p/mod.rs +++ b/coordinator/p2p/src/lib.rs @@ -1,3 +1,7 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + use core::future::Future; use borsh::{BorshSerialize, BorshDeserialize}; @@ -8,20 +12,21 @@ use tokio::sync::oneshot; use serai_cosign::SignedCosign; -/// The libp2p-backed P2P network -mod libp2p; - /// The heartbeat task, effecting sync of Tributaries -mod heartbeat; +pub mod heartbeat; /// A tributary block and its commit. #[derive(Clone, BorshSerialize, BorshDeserialize)] -pub(crate) struct TributaryBlockWithCommit { - pub(crate) block: Vec, - pub(crate) commit: Vec, +pub struct TributaryBlockWithCommit { + /// The serialized block. + pub block: Vec, + /// The serialized commit. + pub commit: Vec, } -trait Peer<'a>: Send { +/// A representation of a peer. +pub trait Peer<'a>: Send { + /// Send a heartbeat to this peer. fn send_heartbeat( &self, set: ValidatorSet, @@ -29,7 +34,9 @@ trait Peer<'a>: Send { ) -> impl Send + Future>>; } -trait P2p: Send + Sync + tributary::P2p + serai_cosign::RequestNotableCosigns { +/// The representation of the P2P network. +pub trait P2p: Send + Sync + Clone + tributary::P2p + serai_cosign::RequestNotableCosigns { + /// The representation of a peer. type Peer<'a>: Peer<'a>; /// Fetch the peers for this network. diff --git a/coordinator/src/main.rs b/coordinator/src/main.rs index 2316af2e..9d555b71 100644 --- a/coordinator/src/main.rs +++ b/coordinator/src/main.rs @@ -1,5 +1,9 @@ mod tributary; -mod p2p; + +mod p2p { + use serai_coordinator_p2p::*; + pub use serai_coordinator_libp2p_p2p::Libp2p; +} fn main() { todo!("TODO") diff --git a/deny.toml b/deny.toml index fa12461c..f530b6a2 100644 --- a/deny.toml +++ b/deny.toml @@ -75,6 +75,8 @@ exceptions = [ { allow = ["AGPL-3.0"], name = "tributary-chain" }, { allow = ["AGPL-3.0"], name = "serai-cosign" }, { allow = ["AGPL-3.0"], name = "serai-coordinator-substrate" }, + { allow = ["AGPL-3.0"], name = "serai-coordinator-p2p" }, + { allow = ["AGPL-3.0"], name = "serai-coordinator-libp2p-p2p" }, { allow = ["AGPL-3.0"], name = "serai-coordinator" }, { allow = ["AGPL-3.0"], name = "serai-coins-pallet" },