Skip to content
Draft
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
73 changes: 68 additions & 5 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ members = [
"transports/websocket-websys",
"transports/websocket",
"transports/webtransport-websys",
"wasm-tests/webtransport-tests",
"wasm-tests/webtransport-tests", "gossipsub-tests",
]
resolver = "2"

Expand Down
13 changes: 13 additions & 0 deletions Dockerfile.gossipsub-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM rust:1.88-bullseye AS builder
WORKDIR /usr/src/libp2p

RUN apt-get update

COPY . .

RUN cd ./gossipsub-tests && cargo build --release

FROM debian:bullseye-slim
COPY --from=builder /usr/src/libp2p/target/release/gossipsub-tests /usr/local/bin/gossipsub-tests

ENTRYPOINT ["gossipsub-tests"]
12 changes: 12 additions & 0 deletions gossipsub-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "gossipsub-tests"
version = "0.1.0"
edition = "2021"

[dependencies]
if-addrs = "0.14"
libp2p = { path = "../libp2p", default-features = false, features = ["dns", "tcp", "tokio", "gossipsub", "ed25519", "noise", "yamux"] }
redis = { version = "0.32", features = ["tokio-comp"] }
tokio = { version = "1.46", features = [ "macros"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
24 changes: 24 additions & 0 deletions gossipsub-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# gossipsub-tests

A test suite for verifying the behavior of gossipsub protocol.

## Build and Run

**Note: All commands below should be run from the rust-libp2p repository root directory.**

Build the Docker image:
```bash
docker build -f Dockerfile.gossipsub-tests -t gossipsub-tests .
```

Run `subscribe` test case using Kurtosis:
```bash
# Run `subscribe` test
kurtosis run -v brief --enclave gossipsub github.com/ackintosh/gossipsub-package --args-file gossipsub-tests/subscribe.yaml

# Inspect the running enclave and node status
kurtosis enclave inspect gossipsub

# Export logs for analysis
kurtosis dump gossipsub-test-logs
```
28 changes: 28 additions & 0 deletions gossipsub-tests/src/addr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use libp2p::multiaddr::Protocol;
use libp2p::Multiaddr;

/// Creates a listener multiaddress for the local machine with the specified TCP port.
pub(crate) fn get_listener_addr(port: u16) -> Result<Multiaddr, &'static str> {
let mut addr = get_local_multiaddr()?;
addr.push(Protocol::Tcp(port));
Ok(addr)
}

/// Gets the local machine's non-loopback IPv4 address as a multiaddress.
fn get_local_multiaddr() -> Result<Multiaddr, &'static str> {
let Ok(ifs) = if_addrs::get_if_addrs() else {
return Err("Failed to get interfaces.");
};

let addrs = ifs
.iter()
.filter(|i| !i.addr.is_loopback() && i.addr.ip().is_ipv4())
.map(|i| Multiaddr::try_from(i.addr.ip()).unwrap())
.collect::<Vec<_>>();

if addrs.len() > 1 {
return Err("Failed to get interfaces.");
}

Ok(addrs[0].clone())
}
84 changes: 84 additions & 0 deletions gossipsub-tests/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::addr::get_listener_addr;
use crate::node::collect_addr;
use crate::redis::RedisClient;
use crate::subscribe::subscribe;
use libp2p::Multiaddr;
use tracing::info;

mod addr;
mod node;
mod redis;
mod subscribe;

#[tokio::main]
async fn main() {
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
}

let context = Context::new().await;
info!("Context:");
info!(" test_name: {}", context.test_name);
info!(" node_count: {}", context.node_count);
info!(" node_index: {}", context.node_index);
info!(" local_addr: {}", context.local_addr);
info!(" participants:");
for (i, participant) in context.participants.iter().enumerate() {
info!(" [{i}]: {participant}");
}

// Execute the specified test scenario
match context.test_name.as_str() {
"subscribe" => subscribe(context).await,
test_name => unreachable!("Unknown test name: {test_name}"),
};
}

/// Test execution context containing configuration and coordination resources.
///
/// Holds all necessary information for a test node including Redis connection
/// for inter-node coordination, network addresses, and test parameters.
struct Context {
test_name: String,
redis: RedisClient,
local_addr: Multiaddr,
participants: Vec<Multiaddr>,
node_count: u64,
node_index: u64,
}

impl Context {
async fn new() -> Self {
let redis_host = std::env::var("REDIS_HOST").expect("REDIS_HOST must be set");
let redis_port = std::env::var("REDIS_PORT")
.expect("REDIS_PORT must be set")
.parse()
.expect("REDIS_PORT must be a valid number");
let mut redis = RedisClient::new(redis_host, redis_port)
.await
.expect("Connect to redis");

let node_count: u64 = std::env::var("NODE_COUNT")
.expect("NODE_COUNT must be set")
.parse()
.expect("NODE_COUNT must be a valid number");

let local_addr = get_listener_addr(9000).unwrap();
let participants = collect_addr(&mut redis, local_addr.clone(), node_count as usize).await;

Context {
test_name: std::env::var("TEST_NAME").expect("TEST_NAME must be set"),
redis,
local_addr,
participants,
node_count: std::env::var("NODE_COUNT")
.expect("NODE_COUNT must be set")
.parse()
.expect("NODE_COUNT must be a valid number"),
node_index: std::env::var("NODE_INDEX")
.expect("NODE_INDEX must be set")
.parse()
.expect("NODE_INDEX must be a valid number"),
}
}
}
Loading
Loading