/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2026 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 . */ use futures::{ future::{join_all, select, Either}, pin_mut, }; use smol::{lock::RwLock as AsyncRwLock, Executor, Timer}; use std::{ sync::Arc, time::{Duration, UNIX_EPOCH}, }; use tracing::debug; use super::super::{ channel::ChannelPtr, message::{VerackMessage, VersionMessage}, message_publisher::MessageSubscription, settings::Settings, }; use crate::{ net::{session::SESSION_OUTBOUND, BanPolicy}, util::logger::verbose, Error, Result, }; /// Implements the protocol version handshake sent out by nodes at /// the beginning of a connection. pub struct ProtocolVersion { channel: ChannelPtr, version_sub: MessageSubscription, verack_sub: MessageSubscription, settings: Arc>, } impl ProtocolVersion { /// Create a new version protocol. Makes a version and version ack /// subscription, then adds them to a version protocol instance. // TODO: This function takes settings as a param, however, it is also reachable through Channel. // Maybe we want to navigate towards Settings through channel->session->p2p->settings pub async fn new(channel: ChannelPtr, settings: Arc>) -> Arc { // Creates a version subscription let version_sub = channel.subscribe_msg::().await.expect("Missing version dispatcher!"); // Creates a version acknowledgement subscription let verack_sub = channel.subscribe_msg::().await.expect("Missing verack dispatcher!"); Arc::new(Self { channel, version_sub, verack_sub, settings }) } /// Start version information exchange. Start the timer. Send version /// info and wait for version ack. Wait for version info and send /// version ack. pub async fn run(self: Arc, executor: Arc>) -> Result<()> { debug!(target: "net::protocol_version::run", "START => address={}", self.channel.display_address()); let channel_handshake_timeout = self.settings.read().await.channel_handshake_timeout(self.channel.address().scheme()); let timeout = Timer::after(Duration::from_secs(channel_handshake_timeout)); let version = self.clone().exchange_versions(executor); pin_mut!(timeout); pin_mut!(version); // Run timer and version exchange at the same time. Either deal // with the success or failure of the version exchange or // time out. match select(version, timeout).await { Either::Left((Ok(_), _)) => { debug!(target: "net::protocol_version::run", "END => address={}", self.channel.display_address()); Ok(()) } Either::Left((Err(e), _)) => { verbose!( target: "net::protocol_version::run", "[P2P] Version Exchange failed [{}]: {e}", self.channel.display_address() ); self.channel.stop().await; Err(e) } Either::Right((_, _)) => { verbose!( target: "net::protocol_version::run", "[P2P] Version Exchange timed out [{}]", self.channel.display_address(), ); self.channel.stop().await; Err(Error::ChannelTimeout) } } } /// Send and receive version information async fn exchange_versions(self: Arc, executor: Arc>) -> Result<()> { debug!( target: "net::protocol_version::exchange_versions", "START => address={}", self.channel.display_address(), ); let send = executor.spawn(self.clone().send_version()); let recv = executor.spawn(self.clone().recv_version()); let rets = join_all(vec![send, recv]).await; if let Err(e) = &rets[0] { verbose!( target: "net::protocol_version::exchange_versions", "send_version() failed: {e}" ); return Err(e.clone()) } if let Err(e) = &rets[1] { verbose!( target: "net::protocol_version::exchange_versions", "recv_version() failed: {e}" ); return Err(e.clone()) } debug!( target: "net::protocol_version::exchange_versions", "END => address={}", self.channel.display_address(), ); Ok(()) } /// Send version info and wait for version acknowledgement. /// Ensures that the app version is the same. async fn send_version(self: Arc) -> Result<()> { debug!( target: "net::protocol_version::send_version", "START => address={}", self.channel.display_address(), ); let settings = self.settings.read().await; let node_id = settings.node_id.clone(); let app_version = settings.app_version.clone(); let app_name = settings.app_name.clone(); drop(settings); let external_addrs = self.channel.hosts().external_addrs().await; let version = VersionMessage { node_id, app_name: app_name.clone(), version: app_version.clone(), timestamp: UNIX_EPOCH.elapsed().unwrap().as_secs(), connect_recv_addr: self.channel.connect_addr().clone(), resolve_recv_addr: self.channel.resolve_addr(), ext_send_addr: external_addrs, /* NOTE: `features` is a list of enabled features in the format Vec<(service, version)>. In the future, Protocols will add their own data to this field when they are attached.*/ features: vec![], }; self.channel.send(&version).await?; // Wait for verack let verack_msg = self.verack_sub.receive().await?; // Validate peer received version against our version. debug!( target: "net::protocol_version::send_version", "App version: {app_version}, Recv version: {}", verack_msg.app_version, ); // MAJOR and MINOR should be the same, as well as the app identifier if app_version.major != verack_msg.app_version.major || app_version.minor != verack_msg.app_version.minor || app_name != verack_msg.app_name { verbose!( target: "net::protocol_version::send_version", "[P2P] Version mismatch from {}. Disconnecting...", self.channel.display_address(), ); // If it is outbound, ban the host so we don't share it with other nodes if self.channel.session_type_id() & SESSION_OUTBOUND != 0 { if let BanPolicy::Strict = self.channel.p2p().settings().read().await.ban_policy { self.channel.ban().await; } } self.channel.stop().await; return Err(Error::ChannelStopped) } // Versions are compatible debug!( target: "net::protocol_version::send_version", "END => address={}", self.channel.display_address(), ); Ok(()) } /// Receive version info, check the message is okay and send verack /// with app version attached. async fn recv_version(self: Arc) -> Result<()> { debug!( target: "net::protocol_version::recv_version", "START => address={}", self.channel.display_address(), ); // Receive version message let version = self.version_sub.receive().await?; if let Some(ipv6_addr) = version.get_ipv6_addr() { let hosts = self.channel.p2p().hosts(); hosts.add_auto_addr(ipv6_addr); } self.channel.set_version(version).await; // Send verack let settings = self.settings.read().await; let app_version = settings.app_version.clone(); let app_name = settings.app_name.clone(); drop(settings); let verack = VerackMessage { app_version, app_name }; self.channel.send(&verack).await?; debug!( target: "net::protocol_version::recv_version", "END => address={}", self.channel.display_address(), ); Ok(()) } }