Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/rustsec-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ jobs:
# TODO: Remove second once Substrate upgrades litep2p and sc-network and we no longer have ring 0.16 in our dependencies
# TODO: Remove third once Substrate upgrades wasmtime and we no longer have wasmtime <= 9 in our dependencies
# TODO: Remove fourth once Substrate upgrades wasmtime and we no longer have wasmtime <= 23 in our dependencies
ignore: RUSTSEC-2024-0336, RUSTSEC-2025-0009, RUSTSEC-2023-0091, RUSTSEC-2024-0438
# TODO: Remove fifth once Substrate upgrades wasmtime and we no longer have wasmtime < 24.0.5 in our dependencies (RUSTSEC-2025-0118)
ignore: RUSTSEC-2024-0336, RUSTSEC-2025-0009, RUSTSEC-2023-0091, RUSTSEC-2024-0438, RUSTSEC-2025-0118
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/.idea
/.vscode
/target
/test/subspace-test-fuzzer/target
/test/subspace-test-fuzzer/output
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [
"test/subspace-test-fuzzer",
"test/subspace-test-runtime",
"test/subspace-test-service",
"test/subspace-farmerless-dev-node",
]

[workspace.dependencies]
Expand Down
27 changes: 27 additions & 0 deletions domains/test/service/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, NumberFor};
use sp_session::SessionKeys;
use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
use std::future::Future;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use subspace_runtime_primitives::opaque::Block as CBlock;
Expand Down Expand Up @@ -146,6 +147,8 @@ where
maybe_operator_id: Option<OperatorId>,
role: Role,
mock_consensus_node: &mut MockConsensusNode,
rpc_addr: Option<SocketAddr>,
rpc_port: Option<u16>,
) -> Self {
let mut domain_config = node_config(
domain_id,
Expand All @@ -156,6 +159,8 @@ where
role,
base_path.clone(),
Box::new(create_domain_spec()) as Box<_>,
rpc_addr,
rpc_port,
)
.expect("could not generate domain node Configuration");

Expand Down Expand Up @@ -573,6 +578,8 @@ pub struct DomainNodeBuilder {
skip_empty_bundle_production: bool,
base_path: BasePath,
maybe_operator_id: Option<OperatorId>,
rpc_addr: Option<SocketAddr>,
rpc_port: Option<u16>,
}

impl DomainNodeBuilder {
Expand All @@ -588,6 +595,8 @@ impl DomainNodeBuilder {
skip_empty_bundle_production: false,
base_path,
maybe_operator_id: None,
rpc_addr: None,
rpc_port: None,
}
}

Expand Down Expand Up @@ -620,6 +629,18 @@ impl DomainNodeBuilder {
self
}

/// Set RPC address for the domain node
pub fn rpc_addr(mut self, addr: SocketAddr) -> Self {
self.rpc_addr = Some(addr);
self
}

/// Set RPC port for the domain node
pub fn rpc_port(mut self, port: u16) -> Self {
self.rpc_port = Some(port);
self
}

/// Build an EVM domain node
pub async fn build_evm_node(
self,
Expand All @@ -642,6 +663,8 @@ impl DomainNodeBuilder {
self.maybe_operator_id,
role,
mock_consensus_node,
self.rpc_addr,
self.rpc_port,
)
.await
}
Expand All @@ -668,6 +691,8 @@ impl DomainNodeBuilder {
self.maybe_operator_id,
role,
mock_consensus_node,
self.rpc_addr,
self.rpc_port,
)
.await
}
Expand All @@ -691,6 +716,8 @@ impl DomainNodeBuilder {
self.maybe_operator_id,
role,
mock_consensus_node,
self.rpc_addr,
self.rpc_port,
)
.await
}
Expand Down
75 changes: 58 additions & 17 deletions domains/test/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use sc_network::multiaddr;
use sc_service::config::{
DatabaseSource, ExecutorConfiguration, KeystoreConfig, MultiaddrWithPeerId,
NetworkConfiguration, OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig,
RpcConfiguration, WasmExecutionMethod, WasmtimeInstantiationStrategy,
RpcConfiguration, RpcEndpoint, WasmExecutionMethod, WasmtimeInstantiationStrategy,
};
use sc_service::{
BasePath, BlocksPruning, ChainSpec, Configuration as ServiceConfiguration,
Expand All @@ -48,6 +48,7 @@ use sp_runtime::generic;
use sp_runtime::generic::SignedPayload;
use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable};
use std::fmt::{Debug, Display};
use std::net::SocketAddr;
use std::str::FromStr;

/// The domain id of the evm domain
Expand All @@ -72,6 +73,8 @@ pub fn node_config(
role: Role,
base_path: BasePath,
chain_spec: Box<dyn ChainSpec>,
rpc_addr: Option<SocketAddr>,
rpc_port: Option<u16>,
) -> Result<ServiceConfiguration, ServiceError> {
let root = base_path.path().to_path_buf();

Expand Down Expand Up @@ -104,6 +107,59 @@ pub fn node_config(

network_config.transport = TransportConfig::MemoryOnly;

let rpc_configuration = match rpc_addr {
Some(listen_addr) => {
let port = rpc_port.unwrap_or(9945);
RpcConfiguration {
addr: Some(vec![RpcEndpoint {
batch_config: RpcBatchRequestConfig::Disabled,
max_connections: 100,
listen_addr,
rpc_methods: Default::default(),
rate_limit: None,
rate_limit_trust_proxy_headers: false,
rate_limit_whitelisted_ips: vec![],
max_payload_in_mb: 15,
max_payload_out_mb: 15,
max_subscriptions_per_connection: 100,
max_buffer_capacity_per_connection: 100,
cors: None,
retry_random_port: true,
is_optional: false,
}]),
max_request_size: 15,
max_response_size: 15,
id_provider: None,
max_subs_per_conn: 1024,
port,
message_buffer_capacity: 1024,
batch_config: RpcBatchRequestConfig::Disabled,
max_connections: 1000,
cors: None,
methods: Default::default(),
rate_limit: None,
rate_limit_whitelisted_ips: vec![],
rate_limit_trust_proxy_headers: false,
}
}
None => RpcConfiguration {
addr: None,
max_request_size: 0,
max_response_size: 0,
id_provider: None,
max_subs_per_conn: 0,
port: 0,
message_buffer_capacity: 0,
batch_config: RpcBatchRequestConfig::Disabled,
max_connections: 0,
cors: None,
methods: Default::default(),
rate_limit: None,
rate_limit_whitelisted_ips: vec![],
rate_limit_trust_proxy_headers: false,
},
};

Ok(ServiceConfiguration {
impl_name: "domain-test-node".to_string(),
impl_version: "0.1".to_string(),
Expand All @@ -127,22 +183,7 @@ pub fn node_config(
default_heap_pages: None,
runtime_cache_size: 2,
},
rpc: RpcConfiguration {
addr: None,
max_request_size: 0,
max_response_size: 0,
id_provider: None,
max_subs_per_conn: 0,
port: 0,
message_buffer_capacity: 0,
batch_config: RpcBatchRequestConfig::Disabled,
max_connections: 0,
cors: None,
methods: Default::default(),
rate_limit: None,
rate_limit_whitelisted_ips: vec![],
rate_limit_trust_proxy_headers: false,
},
rpc: rpc_configuration,
prometheus_config: None,
telemetry_endpoints: None,
offchain_worker: OffchainWorkerConfig {
Expand Down
25 changes: 25 additions & 0 deletions test/subspace-farmerless-dev-node/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "subspace-farmerless-dev-node"
version = "0.1.0"
edition = "2024"
license = "GPL-3.0-or-later"
publish = false

[[bin]]
name = "subspace-farmerless-dev-node"
path = "src/main.rs"

[dependencies]
clap = { workspace = true, features = ["derive"] }
sc-cli = { workspace = true, default-features = false }
sc-service = { workspace = true, default-features = false }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tempfile = { workspace = true }
tracing = { workspace = true }
sp-keyring = { workspace = true }
jsonrpsee = { workspace = true, features = ["macros", "server-core"] }
async-trait = { workspace = true }

subspace-test-service = { workspace = true }
domain-test-service = { workspace = true }

83 changes: 83 additions & 0 deletions test/subspace-farmerless-dev-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Subspace Farmerless Dev Node

Developer utility binary that boots a local Subspace consensus node (mocked farmerless setup) and, optionally, an EVM domain node for integration testing and manual experimentation. It wraps helpers from `subspace-test-service` and `domain-test-service` so that devs can exercise cross-domain flows without spinning up a full farmer.

## Prerequisites

- Rust toolchain with `cargo`
- Workspace dependencies built once (`cargo build -p subspace-farmerless-dev-node`)

## Usage

From the workspace root:

```
cargo run -p subspace-farmerless-dev-node -- [FLAGS/OPTIONS]
```

Common flags:

- `--finalize-depth <K>`: Enforce finalization depth; omit to disable.
- `--domain`: Start the EVM domain node alongside consensus.
- `--base-path <PATH>`: Persist data instead of using a temp dir.
- `--rpc-host <IP>` / `--rpc-port <PORT>`: Consensus RPC interface (defaults `127.0.0.1:9944`).
- `--domain-rpc-host <IP>` / `--domain-rpc-port <PORT>`: Domain RPC interface (defaults `127.0.0.1:9945`).
- `--block-interval-ms <MS>`: Slot and block production cadence (default `6000`; use `0` to disable auto-production).

To inspect the full CLI help, run:

```
cargo run -p subspace-farmerless-dev-node -- --help
```

## Typical Scenarios

- **Quick smoke test:** `cargo run -p subspace-farmerless-dev-node` (produces consensus blocks every 6s using temp storage).
- **Run with domain node:** `cargo run -p subspace-farmerless-dev-node -- --domain`.
- **Fast integration testing:** `cargo run -p subspace-farmerless-dev-node -- --block-interval-ms 500 --domain`.
- **Manual block production:** `cargo run -p subspace-farmerless-dev-node -- --block-interval-ms 0 --domain` and trigger blocks via RPC helpers.

## Manual Block Production RPCs

When `--block-interval-ms 0` is set, the node exposes JSON-RPC endpoints for manual block production:

### `dev_produceBlock`

Produce a single consensus block.

**Parameters:**

- `wait_for_bundle` (optional, boolean): If `true`, wait for domain bundle submission before producing the block. Defaults to `false`.

**Example:**

```bash
curl -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":1,"method":"dev_produceBlock","params":[true]}' \
http://127.0.0.1:9944
```

### `dev_produceBlocks`

Produce multiple consensus blocks.

**Parameters:**

- `count` (required, number): Number of blocks to produce.
- `wait_for_bundle` (optional, boolean): If `true`, wait for domain bundle submission before each block. Defaults to `false`.

**Example:**

```bash
# Produce 5 blocks without waiting for bundles
curl -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":1,"method":"dev_produceBlocks","params":[5]}' \
http://127.0.0.1:9944

# Produce 5 blocks, waiting for bundles
curl -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":1,"method":"dev_produceBlocks","params":[5,true]}' \
http://127.0.0.1:9944
```

**Note:** When `wait_for_bundle` is `true`, the domain node must be running (`--domain` flag) or the RPC call will timeout waiting for bundle submission.
Loading
Loading