subspace_service/
config.rs

1use crate::dsn::DsnConfig;
2use sc_chain_spec::ChainSpec;
3use sc_consensus_subspace::archiver::CreateObjectMappings;
4use sc_network::config::{
5    DEFAULT_KADEMLIA_REPLICATION_FACTOR, MultiaddrWithPeerId, NetworkBackendType,
6    NetworkConfiguration, NodeKeyConfig, SetConfig, SyncMode, TransportConfig,
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::Arc;
25use std::sync::atomic::AtomicBool;
26use subspace_data_retrieval::piece_getter::PieceGetter;
27use subspace_networking::Node;
28use subspace_networking::libp2p::Multiaddr;
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                network_backend: NetworkBackendType::Libp2p,
208                force_synced: configuration.network.force_synced,
209            },
210            // Not used on consensus chain
211            keystore: KeystoreConfig::InMemory,
212            database: DatabaseSource::ParityDb {
213                path: configuration.base_path.join("db"),
214            },
215            data_path: configuration.base_path.clone(),
216            trie_cache_maximum_size: configuration.trie_cache_size,
217            state_pruning: Some(configuration.state_pruning),
218            blocks_pruning: configuration.blocks_pruning,
219            executor: configuration.executor,
220            wasm_runtime_overrides: None,
221            rpc: RpcConfiguration {
222                addr: rpc_addr,
223                methods: configuration.rpc_options.methods,
224                max_connections: configuration.rpc_options.max_connections,
225                cors: configuration.rpc_options.cors,
226                max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB,
227                max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB,
228                id_provider: None,
229                max_subs_per_conn: configuration.rpc_options.max_subscriptions_per_connection,
230                // Doesn't matter since we have specified address above
231                port: 0,
232                message_buffer_capacity: configuration
233                    .rpc_options
234                    .message_buffer_capacity_per_connection,
235                batch_config: rpc_batch_config,
236                rate_limit: configuration.rpc_options.rate_limit,
237                rate_limit_whitelisted_ips: configuration.rpc_options.rate_limit_whitelisted_ips,
238                rate_limit_trust_proxy_headers: configuration
239                    .rpc_options
240                    .rate_limit_trust_proxy_headers,
241            },
242            prometheus_config: configuration
243                .prometheus_listen_on
244                .map(|prometheus_listen_on| {
245                    PrometheusConfig::new_with_default_registry(
246                        prometheus_listen_on,
247                        configuration.chain_spec.id().to_string(),
248                    )
249                }),
250            telemetry_endpoints: configuration.telemetry_endpoints,
251            // Offchain worker is not used
252            // indexing is used to store the mmr leaves from Runtime
253            offchain_worker: OffchainWorkerConfig {
254                enabled: false,
255                indexing_enabled: true,
256            },
257            force_authoring: configuration.force_authoring,
258            disable_grandpa: true,
259            dev_key_seed: None,
260            tracing_targets: None,
261            tracing_receiver: Default::default(),
262            chain_spec: configuration.chain_spec,
263            // Substrate's default
264            announce_block: true,
265            role: if configuration.farmer {
266                sc_service::Role::Authority
267            } else {
268                sc_service::Role::Full
269            },
270            base_path: BasePath::new(configuration.base_path),
271        }
272    }
273}
274
275/// Subspace networking instantiation variant
276#[derive(Debug)]
277#[allow(clippy::large_enum_variant)]
278pub enum SubspaceNetworking {
279    /// Use existing networking instance
280    Reuse {
281        /// Node instance
282        node: Node,
283        /// Bootstrap nodes used (that can be also sent to the farmer over RPC)
284        bootstrap_nodes: Vec<Multiaddr>,
285        /// Piece getter
286        piece_getter: Arc<dyn PieceGetter + Send + Sync + 'static>,
287    },
288    /// Networking must be instantiated internally
289    Create {
290        /// Configuration to use for DSN instantiation
291        config: DsnConfig,
292    },
293}
294
295/// Subspace-specific service configuration.
296#[derive(Debug)]
297pub struct SubspaceConfiguration {
298    /// Base configuration.
299    pub base: Configuration,
300    /// Whether slot notifications need to be present even if node is not responsible for block
301    /// authoring.
302    pub force_new_slot_notifications: bool,
303    /// Create object mappings from a specified segment index, or disable object mapping creation.
304    pub create_object_mappings: CreateObjectMappings,
305    /// Subspace networking (DSN).
306    pub subspace_networking: SubspaceNetworking,
307    /// Is this node a Timekeeper
308    pub is_timekeeper: bool,
309    /// CPU cores that timekeeper can use
310    pub timekeeper_cpu_cores: HashSet<usize>,
311    /// Defines blockchain sync mode
312    pub sync: ChainSyncMode,
313}
314
315/// Syncing mode.
316#[derive(Debug, Clone, Copy, PartialEq)]
317pub enum ChainSyncMode {
318    /// Full sync. Download and verify all blocks from DSN.
319    Full,
320    /// Download latest state and related blocks only. Run full DSN-sync afterwards.
321    Snap,
322}
323
324impl FromStr for ChainSyncMode {
325    type Err = String;
326
327    fn from_str(input: &str) -> Result<Self, Self::Err> {
328        match input {
329            "full" => Ok(Self::Full),
330            "snap" => Ok(Self::Snap),
331            _ => Err("Unsupported sync type: use full or snap".to_string()),
332        }
333    }
334}
335
336impl fmt::Display for ChainSyncMode {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        match self {
339            Self::Full => f.write_str("full"),
340            Self::Snap => f.write_str("snap"),
341        }
342    }
343}
344
345impl Default for ChainSyncMode {
346    fn default() -> Self {
347        Self::Full
348    }
349}
350
351impl Deref for SubspaceConfiguration {
352    type Target = Configuration;
353
354    fn deref(&self) -> &Self::Target {
355        &self.base
356    }
357}