subspace_malicious_operator/
lib.rs

1//! Subspace malicious operator library.
2
3mod chain_spec;
4mod malicious_bundle_producer;
5mod malicious_bundle_tamper;
6pub mod malicious_domain_instance_starter;
7
8pub use chain_spec::create_domain_spec;
9use clap::Parser;
10use sc_chain_spec::GenericChainSpec;
11use sc_cli::{
12    generate_node_name, ChainSpec, CliConfiguration, Role, RunCmd as SubstrateRunCmd, RunCmd,
13    SubstrateCli,
14};
15use sc_service::config::{
16    ExecutorConfiguration, KeystoreConfig, NetworkConfiguration, RpcConfiguration,
17};
18use sc_service::{BasePath, BlocksPruning, Configuration, DatabaseSource};
19use sp_core::crypto::{AccountId32, Ss58Codec};
20use sp_domains::DomainId;
21
22/// Subspace Cli.
23#[derive(Debug, Parser)]
24#[clap(
25    propagate_version = true,
26    args_conflicts_with_subcommands = true,
27    subcommand_negates_reqs = true
28)]
29pub struct Cli {
30    /// Run a node.
31    #[clap(flatten)]
32    pub run: RunCmd,
33
34    /// Sudo account to use for malicious operator
35    /// If not passed, dev sudo account is used instead.
36    #[arg(long)]
37    pub sudo_account: Option<String>,
38
39    /// Domain arguments
40    ///
41    /// The command-line arguments provided first will be passed to the embedded consensus node,
42    /// while the arguments provided after `--` will be passed to the domain node.
43    ///
44    /// subspace-node [consensus-chain-args] -- [domain-args]
45    #[arg(raw = true)]
46    pub domain_args: Vec<String>,
47}
48
49impl Cli {
50    pub fn sudo_account(&self) -> AccountId32 {
51        self.sudo_account
52            .as_ref()
53            .map(|sudo_account| {
54                AccountId32::from_ss58check(sudo_account).expect("Invalid sudo account")
55            })
56            .unwrap_or(crate::chain_spec::consensus_dev_sudo_account())
57    }
58}
59
60impl SubstrateCli for Cli {
61    fn impl_name() -> String {
62        "Subspace".into()
63    }
64
65    fn impl_version() -> String {
66        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
67    }
68
69    fn executable_name() -> String {
70        // Customize to make sure directory used for data by default is the same regardless of the
71        // name of the executable file.
72        "subspace-node".to_string()
73    }
74
75    fn description() -> String {
76        env!("CARGO_PKG_DESCRIPTION").into()
77    }
78
79    fn author() -> String {
80        env!("CARGO_PKG_AUTHORS").into()
81    }
82
83    fn support_url() -> String {
84        "https://forum.subspace.network".into()
85    }
86
87    fn copyright_start_year() -> i32 {
88        2021
89    }
90
91    fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
92        let chain_spec = match id {
93            "dev" => crate::chain_spec::dev_config()?,
94            path => GenericChainSpec::from_json_file(std::path::PathBuf::from(path))?,
95        };
96
97        Ok(Box::new(chain_spec))
98    }
99}
100
101#[derive(Debug, Parser)]
102pub struct DomainCli {
103    /// Run a domain node.
104    #[clap(flatten)]
105    pub run: SubstrateRunCmd,
106
107    #[clap(long)]
108    pub domain_id: u32,
109
110    /// Additional args for domain.
111    #[clap(raw = true)]
112    additional_args: Vec<String>,
113}
114
115impl DomainCli {
116    /// Constructs a new instance of [`DomainCli`].
117    pub fn new(domain_args: impl Iterator<Item = String>) -> Self {
118        DomainCli::parse_from([Self::executable_name()].into_iter().chain(domain_args))
119    }
120
121    pub fn additional_args(&self) -> impl Iterator<Item = String> {
122        [Self::executable_name()]
123            .into_iter()
124            .chain(self.additional_args.clone())
125    }
126}
127
128impl SubstrateCli for DomainCli {
129    fn impl_name() -> String {
130        "Subspace Domain".into()
131    }
132
133    fn impl_version() -> String {
134        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
135    }
136
137    fn executable_name() -> String {
138        // Customize to make sure directory used for data by default is the same regardless of the
139        // name of the executable file.
140        "subspace-node".to_string()
141    }
142
143    fn description() -> String {
144        "Subspace Domain".into()
145    }
146
147    fn author() -> String {
148        env!("CARGO_PKG_AUTHORS").into()
149    }
150
151    fn support_url() -> String {
152        "https://github.com/autonomys/subspace/issues/new".into()
153    }
154
155    fn copyright_start_year() -> i32 {
156        2022
157    }
158
159    fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
160        // TODO: Fetch the runtime name of `self.domain_id` properly.
161        let runtime_name = "evm";
162        match runtime_name {
163            "evm" => crate::chain_spec::load_domain_chain_spec(id),
164            unknown_name => Err(format!("Unknown runtime: {unknown_name}")),
165        }
166    }
167}
168
169/// Default sub directory to store network config.
170pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network";
171
172/// Create a Configuration object from the current object, port from `sc_cli::create_configuration`
173/// and changed to take `chain_spec` as argument instead of construct one internally.
174#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
175pub fn create_malicious_operator_configuration<Cli: SubstrateCli>(
176    domain_id: DomainId,
177    base_path: BasePath,
178    domain_cli: &DomainCli,
179    chain_spec: Box<dyn ChainSpec>,
180    tokio_handle: tokio::runtime::Handle,
181) -> sc_cli::Result<Configuration> {
182    let domain_cli_args = &domain_cli.run;
183    let is_dev = domain_cli_args.shared_params().is_dev();
184    let role = Role::Authority;
185    let config_dir = base_path.config_dir(chain_spec.id());
186    let net_config_dir = config_dir.join(DEFAULT_NETWORK_CONFIG_PATH);
187    let client_id = Cli::client_id();
188    let node_key = domain_cli_args
189        .node_key_params()
190        .map(|x| x.node_key(&net_config_dir, role, is_dev))
191        .unwrap_or_else(|| Ok(Default::default()))?;
192    let max_runtime_instances = 8;
193    let is_validator = role.is_authority();
194    // The malicious operator has its own internal keystore
195    let keystore = KeystoreConfig::InMemory;
196    let telemetry_endpoints = None;
197    let runtime_cache_size = 2;
198    let mut network = match domain_cli_args.network_params() {
199        Some(network_params) => network_params.network_config(
200            &chain_spec,
201            is_dev,
202            is_validator,
203            Some(net_config_dir),
204            &client_id,
205            generate_node_name().as_str(),
206            node_key,
207            30334,
208        ),
209        None => NetworkConfiguration::new(
210            generate_node_name().as_str(),
211            &client_id,
212            node_key,
213            Some(net_config_dir),
214        ),
215    };
216    if let Some(net_config_path) = &mut network.net_config_path {
217        *net_config_path = base_path.path().join("network");
218    }
219
220    let rpc_addrs: Option<Vec<sc_service::config::RpcEndpoint>> = domain_cli
221        .run
222        .rpc_addr(9945)?
223        .map(|addrs| addrs.into_iter().map(Into::into).collect());
224
225    Ok(Configuration {
226        impl_name: Cli::impl_name(),
227        impl_version: Cli::impl_version(),
228        tokio_handle,
229        transaction_pool: domain_cli_args.transaction_pool(is_dev)?,
230        network,
231        keystore,
232        database: DatabaseSource::ParityDb {
233            path: base_path
234                .path()
235                .join("domains")
236                .join(domain_id.to_string())
237                .join("db"),
238        },
239        data_path: config_dir,
240        trie_cache_maximum_size: domain_cli_args.trie_cache_maximum_size()?,
241        state_pruning: domain_cli_args
242            .pruning_params()
243            .map(|x| x.state_pruning())
244            .unwrap_or_else(|| Ok(Default::default()))?,
245        blocks_pruning: domain_cli_args
246            .pruning_params()
247            .map(|x| x.blocks_pruning())
248            .unwrap_or_else(|| Ok(BlocksPruning::KeepFinalized))?,
249        executor: ExecutorConfiguration {
250            wasm_method: domain_cli_args
251                .import_params()
252                .map(|x| x.wasm_method())
253                .unwrap_or_default(),
254            max_runtime_instances,
255            default_heap_pages: domain_cli_args.default_heap_pages()?,
256            runtime_cache_size,
257        },
258        wasm_runtime_overrides: domain_cli_args
259            .import_params()
260            .map(|x| x.wasm_runtime_overrides())
261            .unwrap_or_default(),
262        rpc: RpcConfiguration {
263            addr: rpc_addrs,
264            methods: domain_cli_args.rpc_methods()?,
265            max_connections: domain_cli_args.rpc_max_connections()?,
266            cors: domain_cli_args.rpc_cors(is_dev)?,
267            max_request_size: 15,
268            max_response_size: 15,
269            id_provider: None,
270            max_subs_per_conn: 1024,
271            port: 9945,
272            message_buffer_capacity: domain_cli_args.rpc_buffer_capacity_per_connection()?,
273            batch_config: domain_cli_args.rpc_batch_config()?,
274            rate_limit: domain_cli_args.rpc_rate_limit()?,
275            rate_limit_whitelisted_ips: vec![],
276            rate_limit_trust_proxy_headers: false,
277        },
278        prometheus_config: domain_cli_args.prometheus_config(9616, &chain_spec)?,
279        telemetry_endpoints,
280        offchain_worker: domain_cli_args
281            .offchain_worker_params()
282            .map(|x| x.offchain_worker(&role))
283            .unwrap_or_else(|| Ok(Default::default()))?,
284        force_authoring: domain_cli_args.force_authoring()?,
285        disable_grandpa: domain_cli_args.disable_grandpa()?,
286        dev_key_seed: domain_cli_args.dev_key_seed(is_dev)?,
287        tracing_targets: domain_cli_args.shared_params().tracing_targets(),
288        tracing_receiver: domain_cli_args.shared_params().tracing_receiver(),
289        chain_spec,
290        announce_block: domain_cli_args.announce_block()?,
291        role,
292        base_path,
293    })
294}