-
Notifications
You must be signed in to change notification settings - Fork 243
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3137 from autonomys/gateway-dsn
Add DSN client to subspace-gateway binary
- Loading branch information
Showing
7 changed files
with
202 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//! DSN config and implementation for the gateway. | ||
|
||
use crate::node_client::{NodeClient, RpcNodeClient}; | ||
use anyhow::anyhow; | ||
use clap::{Parser, ValueHint}; | ||
use subspace_networking::libp2p::kad::Mode; | ||
use subspace_networking::libp2p::Multiaddr; | ||
use subspace_networking::{construct, Config, KademliaMode, Node, NodeRunner}; | ||
use tracing::{debug, info}; | ||
|
||
/// Configuration for network stack | ||
#[derive(Debug, Parser)] | ||
pub(crate) struct NetworkArgs { | ||
/// WebSocket RPC URL of the Subspace node to connect to. | ||
/// | ||
/// This node provides the DSN protocol version, default bootstrap nodes, and piece validation | ||
/// metadata. | ||
#[arg(long, value_hint = ValueHint::Url, default_value = "ws://127.0.0.1:9944")] | ||
node_rpc_url: String, | ||
|
||
/// Multiaddrs of DSN bootstrap nodes to connect to on startup, multiple are supported. | ||
/// | ||
/// The default bootstrap nodes are fetched from the node RPC connection. | ||
#[arg(long)] | ||
pub(crate) dsn_bootstrap_nodes: Vec<Multiaddr>, | ||
|
||
/// Multiaddrs of DSN reserved nodes to maintain a connection to, multiple are supported. | ||
#[arg(long)] | ||
dsn_reserved_peers: Vec<Multiaddr>, | ||
|
||
/// Enable non-global (private, shared, loopback..) addresses in the Kademlia DHT. | ||
/// By default these addresses are excluded from the DHT. | ||
#[arg(long, default_value_t = false)] | ||
pub(crate) allow_private_ips: bool, | ||
|
||
/// Maximum established outgoing swarm connection limit. | ||
#[arg(long, default_value_t = 100)] | ||
pub(crate) out_connections: u32, | ||
|
||
/// Maximum pending outgoing swarm connection limit. | ||
#[arg(long, default_value_t = 100)] | ||
pub(crate) pending_out_connections: u32, | ||
} | ||
|
||
/// Create a DSN network client with the supplied configuration. | ||
// TODO: | ||
// - move this DSN code into a new library part of this crate | ||
// - change NetworkArgs to a struct that's independent of clap | ||
pub async fn configure_network( | ||
NetworkArgs { | ||
node_rpc_url, | ||
mut dsn_bootstrap_nodes, | ||
dsn_reserved_peers, | ||
allow_private_ips, | ||
out_connections, | ||
pending_out_connections, | ||
}: NetworkArgs, | ||
) -> anyhow::Result<(Node, NodeRunner<()>, RpcNodeClient)> { | ||
// TODO: | ||
// - store keypair on disk and allow CLI override | ||
// - cache known peers on disk | ||
// - prometheus telemetry | ||
let default_config = Config::<()>::default(); | ||
|
||
info!(url = %node_rpc_url, "Connecting to node RPC"); | ||
let node_client = RpcNodeClient::new(&node_rpc_url) | ||
.await | ||
.map_err(|error| anyhow!("Failed to connect to node RPC: {error}"))?; | ||
|
||
// The gateway only needs the first part of the farmer info. | ||
let farmer_app_info = node_client | ||
.farmer_app_info() | ||
.await | ||
.map_err(|error| anyhow!("Failed to get farmer app info: {error}"))?; | ||
|
||
// Fall back to the node's bootstrap nodes. | ||
if dsn_bootstrap_nodes.is_empty() { | ||
debug!(dsn_bootstrap_nodes = ?farmer_app_info.dsn_bootstrap_nodes, "Setting DSN bootstrap nodes..."); | ||
dsn_bootstrap_nodes.clone_from(&farmer_app_info.dsn_bootstrap_nodes); | ||
} | ||
|
||
let dsn_protocol_version = hex::encode(farmer_app_info.genesis_hash); | ||
debug!(?dsn_protocol_version, "Setting DSN protocol version..."); | ||
|
||
let config = Config { | ||
protocol_version: dsn_protocol_version, | ||
bootstrap_addresses: dsn_bootstrap_nodes, | ||
reserved_peers: dsn_reserved_peers, | ||
allow_non_global_addresses_in_dht: allow_private_ips, | ||
max_established_outgoing_connections: out_connections, | ||
max_pending_outgoing_connections: pending_out_connections, | ||
kademlia_mode: KademliaMode::Static(Mode::Client), | ||
..default_config | ||
}; | ||
|
||
let (node, node_runner) = construct(config)?; | ||
|
||
Ok((node, node_runner, node_client)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//! Node client implementation that connects to node via RPC (WebSockets) | ||
|
||
use async_trait::async_trait; | ||
use jsonrpsee::core::client::{ClientT, Error as JsonError}; | ||
use jsonrpsee::rpc_params; | ||
use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; | ||
use std::fmt; | ||
use std::sync::Arc; | ||
use subspace_core_primitives::segments::{SegmentHeader, SegmentIndex}; | ||
use subspace_rpc_primitives::FarmerAppInfo; | ||
|
||
/// Node client implementation that connects to node via RPC (WebSockets). | ||
/// | ||
/// This implementation is supposed to be used on local network and not via public Internet due to | ||
/// sensitive contents. | ||
#[derive(Debug, Clone)] | ||
pub struct RpcNodeClient { | ||
client: Arc<WsClient>, | ||
} | ||
|
||
impl RpcNodeClient { | ||
/// Create a new instance of [`NodeClient`]. | ||
pub async fn new(url: &str) -> Result<Self, JsonError> { | ||
let client = Arc::new(WsClientBuilder::default().build(url).await?); | ||
Ok(Self { client }) | ||
} | ||
} | ||
|
||
/// Abstraction of the Node Client | ||
#[async_trait] | ||
pub trait NodeClient: fmt::Debug + Send + Sync + 'static { | ||
/// Get farmer app info | ||
async fn farmer_app_info(&self) -> anyhow::Result<FarmerAppInfo>; | ||
|
||
/// Get segment headers for the segments | ||
#[expect(dead_code, reason = "implementation is incomplete")] | ||
async fn segment_headers( | ||
&self, | ||
segment_indices: Vec<SegmentIndex>, | ||
) -> anyhow::Result<Vec<Option<SegmentHeader>>>; | ||
} | ||
|
||
#[async_trait] | ||
impl NodeClient for RpcNodeClient { | ||
async fn farmer_app_info(&self) -> anyhow::Result<FarmerAppInfo> { | ||
Ok(self | ||
.client | ||
.request("subspace_getFarmerAppInfo", rpc_params![]) | ||
.await?) | ||
} | ||
|
||
async fn segment_headers( | ||
&self, | ||
segment_indices: Vec<SegmentIndex>, | ||
) -> anyhow::Result<Vec<Option<SegmentHeader>>> { | ||
Ok(self | ||
.client | ||
.request("subspace_segmentHeaders", rpc_params![&segment_indices]) | ||
.await?) | ||
} | ||
} |