subspace_service/
config.rs

1use crate::dsn::DsnConfig;
2use sc_chain_spec::ChainSpec;
3use sc_consensus_subspace::archiver::CreateObjectMappings;
4use sc_network::config::{
5    MultiaddrWithPeerId, NetworkBackendType, NetworkConfiguration, NodeKeyConfig, SetConfig,
6    SyncMode, TransportConfig, DEFAULT_KADEMLIA_REPLICATION_FACTOR,
7};
8use sc_service::config::{
9    ExecutorConfiguration, IpNetwork, KeystoreConfig, OffchainWorkerConfig, PrometheusConfig,
10    RpcBatchRequestConfig, RpcConfiguration, RpcEndpoint,
11};
12use sc_service::{
13    BasePath, BlocksPruning, Configuration, DatabaseSource, PruningMode, RpcMethods,
14    TransactionPoolOptions,
15};
16use sc_telemetry::TelemetryEndpoints;
17use std::collections::HashSet;
18use std::fmt;
19use std::net::SocketAddr;
20use std::num::{NonZeroU32, NonZeroUsize};
21use std::ops::Deref;
22use std::path::PathBuf;
23use std::str::FromStr;
24use std::sync::atomic::AtomicBool;
25use std::sync::Arc;
26use subspace_data_retrieval::piece_getter::PieceGetter;
27use subspace_networking::libp2p::Multiaddr;
28use subspace_networking::Node;
29use tokio::runtime::Handle;
30
31/// The default max request size in MB, copied from Substrate
32pub const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
33/// The default max response size in MB, copied from Substrate
34pub const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 15;
35
36/// Simplified RPC configuration used in Substrate
37#[derive(Debug)]
38pub struct SubstrateRpcConfiguration {
39    /// IP and port (TCP) on which to listen for RPC requests
40    pub listen_on: Option<SocketAddr>,
41    /// Maximum number of connections for JSON-RPC server
42    pub max_connections: u32,
43    /// CORS settings for HTTP & WS servers. `None` if all origins are allowed
44    pub cors: Option<Vec<String>>,
45    /// RPC methods to expose (by default only a safe subset or all of them)
46    pub methods: RpcMethods,
47    /// RPC rate limiting (calls/minute) for each connection
48    pub rate_limit: Option<NonZeroU32>,
49    /// Disable RPC rate limiting for certain ip addresses
50    pub rate_limit_whitelisted_ips: Vec<IpNetwork>,
51    /// Trust proxy headers for rate limiting
52    pub rate_limit_trust_proxy_headers: bool,
53    /// Maximum allowed subscriptions per rpc connection
54    pub max_subscriptions_per_connection: u32,
55    /// The number of messages the RPC server is allowed to keep in memory
56    pub message_buffer_capacity_per_connection: u32,
57    /// Disable RPC batch requests
58    pub disable_batch_requests: bool,
59    /// Limit the max length per RPC batch request
60    pub max_batch_request_len: Option<u32>,
61}
62
63/// Simplified network used in Substrate
64#[derive(Debug)]
65pub struct SubstrateNetworkConfiguration {
66    /// Multiaddresses to listen for incoming connections.
67    pub listen_on: Vec<sc_network::Multiaddr>,
68    /// Multiaddresses to advertise. Detected automatically if empty.
69    pub public_addresses: Vec<sc_network::Multiaddr>,
70    /// List of initial node addresses
71    pub bootstrap_nodes: Vec<MultiaddrWithPeerId>,
72    /// The node key configuration, which determines the node's network identity keypair.
73    pub node_key: NodeKeyConfig,
74    /// Configuration for the default set of nodes used for block syncing and transactions.
75    pub default_peers_set: SetConfig,
76    /// Name of the node. Sent over the wire for debugging purposes.
77    pub node_name: String,
78    /// Determines whether we allow keeping non-global (private, shared, loopback..) addresses
79    /// in Kademlia DHT.
80    pub allow_private_ips: bool,
81    /// Sync mode
82    pub sync_mode: ChainSyncMode,
83    /// Parameter that allows node to forcefully assume it is synced, needed for network
84    /// bootstrapping only, as long as two synced nodes remain on the network at any time, this
85    /// doesn't need to be used.
86    pub force_synced: bool,
87}
88
89/// Simplified Substrate configuration that can be converted into [`Configuration`] using
90/// [`From`]/[`Into`].
91#[derive(Debug)]
92pub struct SubstrateConfiguration {
93    /// Implementation name
94    pub impl_name: String,
95    /// Implementation version
96    pub impl_version: String,
97    /// Farmer node
98    pub farmer: bool,
99    /// Base path for node data
100    pub base_path: PathBuf,
101    /// Extrinsic pool configuration
102    pub transaction_pool: TransactionPoolOptions,
103    /// Network configuration
104    pub network: SubstrateNetworkConfiguration,
105    /// State pruning settings
106    pub state_pruning: PruningMode,
107    /// Number of blocks to keep in the db.
108    ///
109    /// NOTE: only finalized blocks are subject for removal!
110    pub blocks_pruning: BlocksPruning,
111    /// RPC configuration
112    pub rpc_options: SubstrateRpcConfiguration,
113    /// IP and port (TCP) to start Prometheus exporter on
114    pub prometheus_listen_on: Option<SocketAddr>,
115    /// Telemetry service URL. `None` if disabled.
116    pub telemetry_endpoints: Option<TelemetryEndpoints>,
117    /// Enable authoring even when offline
118    pub force_authoring: bool,
119    /// Chain specification
120    pub chain_spec: Box<dyn ChainSpec>,
121    /// Executor configuration
122    pub executor: ExecutorConfiguration,
123    /// Trie cache size
124    pub trie_cache_size: Option<usize>,
125}
126
127impl From<SubstrateConfiguration> for Configuration {
128    fn from(configuration: SubstrateConfiguration) -> Self {
129        let net_config_path = configuration.base_path.join("network");
130        let default_peers_set_num_full = configuration.network.default_peers_set.in_peers
131            + configuration.network.default_peers_set.out_peers;
132        let client_version = format!("{}/{}", configuration.impl_name, configuration.impl_version);
133
134        let rpc_batch_config = if configuration.rpc_options.disable_batch_requests {
135            RpcBatchRequestConfig::Disabled
136        } else if let Some(l) = configuration.rpc_options.max_batch_request_len {
137            RpcBatchRequestConfig::Limit(l)
138        } else {
139            RpcBatchRequestConfig::Unlimited
140        };
141        let rpc_addr = configuration.rpc_options.listen_on.map(|listen_addr| {
142            vec![RpcEndpoint {
143                batch_config: rpc_batch_config,
144                max_connections: configuration.rpc_options.max_connections,
145                listen_addr,
146                rpc_methods: configuration.rpc_options.methods,
147                rate_limit: configuration.rpc_options.rate_limit,
148                rate_limit_trust_proxy_headers: configuration
149                    .rpc_options
150                    .rate_limit_trust_proxy_headers,
151                rate_limit_whitelisted_ips: configuration
152                    .rpc_options
153                    .rate_limit_whitelisted_ips
154                    .clone(),
155                max_payload_in_mb: RPC_DEFAULT_MAX_REQUEST_SIZE_MB,
156                max_payload_out_mb: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB,
157                max_subscriptions_per_connection: configuration
158                    .rpc_options
159                    .max_subscriptions_per_connection,
160                max_buffer_capacity_per_connection: configuration
161                    .rpc_options
162                    .message_buffer_capacity_per_connection,
163                cors: configuration.rpc_options.cors.clone(),
164                retry_random_port: true,
165                is_optional: false,
166            }]
167        });
168
169        Self {
170            impl_name: configuration.impl_name,
171            impl_version: configuration.impl_version,
172            tokio_handle: Handle::current(),
173            transaction_pool: configuration.transaction_pool,
174            network: NetworkConfiguration {
175                net_config_path: Some(net_config_path.clone()),
176                listen_addresses: configuration.network.listen_on,
177                public_addresses: configuration.network.public_addresses,
178                boot_nodes: if !configuration.network.bootstrap_nodes.is_empty() {
179                    configuration.network.bootstrap_nodes
180                } else {
181                    configuration.chain_spec.boot_nodes().to_vec()
182                },
183                node_key: configuration.network.node_key,
184                default_peers_set: configuration.network.default_peers_set,
185                default_peers_set_num_full,
186                client_version,
187                node_name: configuration.network.node_name,
188                transport: TransportConfig::Normal {
189                    enable_mdns: false,
190                    allow_private_ip: configuration.network.allow_private_ips,
191                },
192                // Substrate's default
193                max_parallel_downloads: 5,
194                // Substrate's default
195                max_blocks_per_request: 64,
196                sync_mode: SyncMode::Full,
197                pause_sync: Arc::new(AtomicBool::new(false)),
198                // Substrate's default
199                enable_dht_random_walk: true,
200                // Substrate's default
201                allow_non_globals_in_dht: configuration.network.allow_private_ips,
202                // Substrate's default
203                kademlia_disjoint_query_paths: false,
204                kademlia_replication_factor: NonZeroUsize::new(DEFAULT_KADEMLIA_REPLICATION_FACTOR)
205                    .expect("value is a constant; constant is non-zero; qed"),
206                ipfs_server: false,
207                yamux_window_size: None,
208                network_backend: NetworkBackendType::Libp2p,
209                force_synced: configuration.network.force_synced,
210            },
211            // Not used on consensus chain
212            keystore: KeystoreConfig::InMemory,
213            database: DatabaseSource::ParityDb {
214                path: configuration.base_path.join("db"),
215            },
216            data_path: configuration.base_path.clone(),
217            trie_cache_maximum_size: configuration.trie_cache_size,
218            state_pruning: Some(configuration.state_pruning),
219            blocks_pruning: configuration.blocks_pruning,
220            executor: configuration.executor,
221            wasm_runtime_overrides: None,
222            rpc: RpcConfiguration {
223                addr: rpc_addr,
224                methods: configuration.rpc_options.methods,
225                max_connections: configuration.rpc_options.max_connections,
226                cors: configuration.rpc_options.cors,
227                max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB,
228                max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB,
229                id_provider: None,
230                max_subs_per_conn: configuration.rpc_options.max_subscriptions_per_connection,
231                // Doesn't matter since we have specified address above
232                port: 0,
233                message_buffer_capacity: configuration
234                    .rpc_options
235                    .message_buffer_capacity_per_connection,
236                batch_config: rpc_batch_config,
237                rate_limit: configuration.rpc_options.rate_limit,
238                rate_limit_whitelisted_ips: configuration.rpc_options.rate_limit_whitelisted_ips,
239                rate_limit_trust_proxy_headers: configuration
240                    .rpc_options
241                    .rate_limit_trust_proxy_headers,
242            },
243            prometheus_config: configuration
244                .prometheus_listen_on
245                .map(|prometheus_listen_on| {
246                    PrometheusConfig::new_with_default_registry(
247                        prometheus_listen_on,
248                        configuration.chain_spec.id().to_string(),
249                    )
250                }),
251            telemetry_endpoints: configuration.telemetry_endpoints,
252            // Offchain worker is not used
253            // indexing is used to store the mmr leaves from Runtime
254            offchain_worker: OffchainWorkerConfig {
255                enabled: false,
256                indexing_enabled: true,
257            },
258            force_authoring: configuration.force_authoring,
259            disable_grandpa: true,
260            dev_key_seed: None,
261            tracing_targets: None,
262            tracing_receiver: Default::default(),
263            chain_spec: configuration.chain_spec,
264            // Substrate's default
265            announce_block: true,
266            role: if configuration.farmer {
267                sc_service::Role::Authority
268            } else {
269                sc_service::Role::Full
270            },
271            base_path: BasePath::new(configuration.base_path),
272        }
273    }
274}
275
276/// Subspace networking instantiation variant
277#[derive(Debug)]
278#[allow(clippy::large_enum_variant)]
279pub enum SubspaceNetworking {
280    /// Use existing networking instance
281    Reuse {
282        /// Node instance
283        node: Node,
284        /// Bootstrap nodes used (that can be also sent to the farmer over RPC)
285        bootstrap_nodes: Vec<Multiaddr>,
286        /// Piece getter
287        piece_getter: Arc<dyn PieceGetter + Send + Sync + 'static>,
288    },
289    /// Networking must be instantiated internally
290    Create {
291        /// Configuration to use for DSN instantiation
292        config: DsnConfig,
293    },
294}
295
296/// Subspace-specific service configuration.
297#[derive(Debug)]
298pub struct SubspaceConfiguration {
299    /// Base configuration.
300    pub base: Configuration,
301    /// Whether slot notifications need to be present even if node is not responsible for block
302    /// authoring.
303    pub force_new_slot_notifications: bool,
304    /// Create object mappings from a specified segment index, or disable object mapping creation.
305    pub create_object_mappings: CreateObjectMappings,
306    /// Subspace networking (DSN).
307    pub subspace_networking: SubspaceNetworking,
308    /// Is this node a Timekeeper
309    pub is_timekeeper: bool,
310    /// CPU cores that timekeeper can use
311    pub timekeeper_cpu_cores: HashSet<usize>,
312    /// Defines blockchain sync mode
313    pub sync: ChainSyncMode,
314}
315
316/// Syncing mode.
317#[derive(Debug, Clone, Copy, PartialEq)]
318pub enum ChainSyncMode {
319    /// Full sync. Download and verify all blocks from DSN.
320    Full,
321    /// Download latest state and related blocks only. Run full DSN-sync afterwards.
322    Snap,
323}
324
325impl FromStr for ChainSyncMode {
326    type Err = String;
327
328    fn from_str(input: &str) -> Result<Self, Self::Err> {
329        match input {
330            "full" => Ok(Self::Full),
331            "snap" => Ok(Self::Snap),
332            _ => Err("Unsupported sync type: use full or snap".to_string()),
333        }
334    }
335}
336
337impl fmt::Display for ChainSyncMode {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        match self {
340            Self::Full => f.write_str("full"),
341            Self::Snap => f.write_str("snap"),
342        }
343    }
344}
345
346impl Default for ChainSyncMode {
347    fn default() -> Self {
348        Self::Full
349    }
350}
351
352impl Deref for SubspaceConfiguration {
353    type Target = Configuration;
354
355    fn deref(&self) -> &Self::Target {
356        &self.base
357    }
358}