Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci(node-wasm): Add integration tests for node-wasm #420

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ jobs:
- name: Test node crate
run: wasm-pack test --headless --chrome node

- name: Test node-wasm crate
run: wasm-pack test --headless --chrome node-wasm

- name: Build and pack node-wasm
run: wasm-pack build --release --target web node-wasm && wasm-pack pack node-wasm

Expand Down Expand Up @@ -170,6 +167,11 @@ jobs:
- name: Run rpc wasm test
run: wasm-pack test --headless --chrome rpc --features=wasm-bindgen

- name: Test node-wasm crate
# We're running node-wasm tests in release mode to get around a failing debug assertion
# https://github.com/libp2p/rust-libp2p/issues/5618
run: wasm-pack test --headless --release --chrome node-wasm



unused-deps:
Expand Down
4 changes: 3 additions & 1 deletion Cargo.lock

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

10 changes: 5 additions & 5 deletions ci/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
context: .
dockerfile: Dockerfile.validator
environment:
# provide amount of bridge nodes to provision (default: 1)
# provide amount of bridge nodes to provision (default: 2)
- BRIDGE_COUNT=2
volumes:
- credentials:/credentials
Expand All @@ -22,6 +22,8 @@ services:
# provide an id for the bridge node (default: 0)
# each node should have a next natural number starting from 0
- NODE_ID=0
# setting SKIP_AUTH to true disables the use of JWT for authentication
- SKIP_AUTH=true
ports:
- 26658:26658
volumes:
Expand All @@ -38,8 +40,6 @@ services:
# provide an id for the bridge node (default: 0)
# each node should have a next natural number starting from 0
- NODE_ID=1
# setting SKIP_AUTH to true disables the use of JWT for authentication
- SKIP_AUTH=true
ports:
- 36658:26658
volumes:
Expand All @@ -57,9 +57,9 @@ services:
# environment:
# # provide an id for the bridge node (default: 0)
# # each node should have a next natural number starting from 0
# - NODE_ID=1
# - NODE_ID=2
# ports:
# - 36658:26658
# - 46658:26658
# volumes:
# - credentials:/credentials
# - genesis:/genesis
Expand Down
16 changes: 15 additions & 1 deletion ci/run-bridge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ whitelist_localhost_nodes() {
# cargo run -- node -n private -l 0.0.0.0
# docker compose -f ci/docker-compose.yml exec bridge-0 celestia p2p peer-info $lumina_peerid
dasel put -f "$CONFIG_DIR/config.toml" \
-t json -v '["172.18.0.1/24", "172.17.0.1/24", "192.168.0.0/16"]' \
-t json -v '["172.16.0.0/12", "192.168.0.0/16"]' \
'P2P.IPColocationWhitelist'
}

Expand All @@ -61,6 +61,16 @@ write_jwt_token() {
celestia bridge auth admin --p2p.network "$P2P_NETWORK" > "$NODE_JWT_FILE"
}

connect_to_common_bridge() {
# wait for nodes to spin up
sleep 5
# get PeerId of the common node
local peer_id=$(celestia p2p info --url 'ws://bridge-0:26658' | jq -r '.result.id')
# connect to it
echo "Connecting to $peer_id: /dns/bridge-0/tcp/2121"
celestia p2p connect "$peer_id" "/dns/bridge-0/tcp/2121"
}

main() {
# Initialize the bridge node
celestia bridge init --p2p.network "$P2P_NETWORK"
Expand All @@ -76,6 +86,10 @@ main() {
write_jwt_token
# give validator some time to set up
sleep 4
# each node without SKIP_AUTH connects to the one with, so that bridges can discover eachother
if [ ! "$SKIP_AUTH" == "true" ] ; then
connect_to_common_bridge &
fi
# Start the bridge node
echo "Configuration finished. Running a bridge node..."
celestia bridge start \
Expand Down
2 changes: 1 addition & 1 deletion cli/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use tracing::warn;

use crate::common::ArgNetwork;

const CELESTIA_LOCAL_BRIDGE_RPC_ADDR: &str = "ws://localhost:26658";
const CELESTIA_LOCAL_BRIDGE_RPC_ADDR: &str = "ws://localhost:36658";

#[derive(Debug, Parser)]
pub(crate) struct Params {
Expand Down
2 changes: 2 additions & 0 deletions node-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ web-sys = { version = "0.3.70", features = [

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.42"
celestia-rpc = { workspace = true, features = ["wasm-bindgen", "p2p"] }
rexie = "0.6.2"

[package.metadata.docs.rs]
targets = ["wasm32-unknown-unknown"]
Expand Down
128 changes: 128 additions & 0 deletions node-wasm/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,131 @@ impl WasmNodeConfig {
})
}
}

#[cfg(test)]
mod tests {
use super::*;

use std::time::Duration;

use celestia_rpc::{prelude::*, Client};
use celestia_types::p2p::PeerId;
use celestia_types::ExtendedHeader;
use gloo_timers::future::sleep;
use libp2p::{multiaddr::Protocol, Multiaddr};
use rexie::Rexie;
use serde_wasm_bindgen::from_value;
use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_test::wasm_bindgen_test;
use web_sys::MessageChannel;

use crate::worker::NodeWorker;

// uses bridge-0, which has skip-auth enabled
const WS_URL: &str = "ws://127.0.0.1:26658";

#[wasm_bindgen_test]
async fn request_network_head_header() {
remove_database().await.expect("failed to clear db");
let rpc_client = Client::new(WS_URL).await.unwrap();
let bridge_ma = fetch_bridge_webtransport_multiaddr(&rpc_client).await;

let client = spawn_connected_node(vec![bridge_ma.to_string()]).await;

let info = client.network_info().await.unwrap();
assert_eq!(info.num_peers, 1);

let bridge_head_header = rpc_client.header_network_head().await.unwrap();
let head_header: ExtendedHeader =
from_value(client.request_head_header().await.unwrap()).unwrap();
assert_eq!(head_header, bridge_head_header);
rpc_client
.p2p_close_peer(&PeerId(
client.local_peer_id().await.unwrap().parse().unwrap(),
))
.await
.unwrap();
}

#[wasm_bindgen_test]
async fn discover_network_peers() {
crate::utils::setup_logging();
remove_database().await.expect("failed to clear db");
let rpc_client = Client::new(WS_URL).await.unwrap();
let bridge_ma = fetch_bridge_webtransport_multiaddr(&rpc_client).await;

let client = spawn_connected_node(vec![bridge_ma.to_string()]).await;

let info = client.network_info().await.unwrap();
assert_eq!(info.num_peers, 1);

sleep(Duration::from_millis(300)).await;

client.wait_connected().await.unwrap();
let info = client.network_info().await.unwrap();
assert_eq!(info.num_peers, 2);
rpc_client
.p2p_close_peer(&PeerId(
client.local_peer_id().await.unwrap().parse().unwrap(),
))
.await
.unwrap();
}

async fn spawn_connected_node(bootnodes: Vec<String>) -> NodeClient {
let message_channel = MessageChannel::new().unwrap();
let mut worker = NodeWorker::new(message_channel.port1().into());

spawn_local(async move {
worker.run().await.unwrap();
});

let client = NodeClient::new(message_channel.port2().into())
.await
.unwrap();
assert!(!client.is_running().await.expect("node ready to be run"));

client
.start(&WasmNodeConfig {
network: Network::Private,
bootnodes,
custom_syncing_window_secs: None,
})
.await
.unwrap();
assert!(client.is_running().await.expect("running node"));
client.wait_connected_trusted().await.expect("to connect");

client
}

async fn fetch_bridge_webtransport_multiaddr(client: &Client) -> Multiaddr {
let bridge_info = client.p2p_info().await.unwrap();

let mut ma = bridge_info
.addrs
.into_iter()
.find(|ma| {
let not_localhost = !ma
.iter()
.any(|prot| prot == Protocol::Ip4("127.0.0.1".parse().unwrap()));
let webtransport = ma
.protocol_stack()
.any(|protocol| protocol == "webtransport");
not_localhost && webtransport
zvolin marked this conversation as resolved.
Show resolved Hide resolved
})
.expect("Bridge doesn't listen on webtransport");

if !ma.protocol_stack().any(|protocol| protocol == "p2p") {
ma.push(Protocol::P2p(bridge_info.id.into()))
}

ma
}

async fn remove_database() -> rexie::Result<()> {
Rexie::delete("private").await?;
Rexie::delete("private-blockstore").await?;
Ok(())
}
}
6 changes: 5 additions & 1 deletion node-wasm/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ pub enum WorkerError {
NodeError(Error),
}

/// `NodeWorker` is responsible for receiving commands from connected [`NodeClient`]s, executing
/// them and sending a response back, as well as accepting new `NodeClient` connections.
///
/// [`NodeClient`]: crate::client::NodeClient
#[wasm_bindgen]
struct NodeWorker {
pub struct NodeWorker {
event_channel_name: String,
node: Option<NodeWorkerInstance>,
request_server: WorkerServer,
Expand Down
2 changes: 1 addition & 1 deletion node/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async fn connects_to_the_go_bridge_node() {
let (node, _) = new_connected_node().await;

let info = node.network_info().await.unwrap();
assert_eq!(info.num_peers(), 1);
assert!(info.num_peers() >= 1);
}

#[tokio::test]
Expand Down
2 changes: 1 addition & 1 deletion node/tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use lumina_node::{node::Node, store::InMemoryStore};
use tokio::sync::Mutex;
use tokio::time::sleep;

const WS_URL: &str = "ws://localhost:26658";
const WS_URL: &str = "ws://localhost:36658";

pub async fn bridge_client() -> Client {
let _ = dotenvy::dotenv();
Expand Down
2 changes: 1 addition & 1 deletion rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use celestia_types::{AppVersion, Blob, TxConfig, nmt::Namespace};
async fn submit_blob() {
// create a client to the celestia node
let token = std::env::var("CELESTIA_NODE_AUTH_TOKEN").expect("Token not provided");
let client = Client::new("ws://localhost:26658", Some(&token))
let client = Client::new("ws://localhost:36658", Some(&token))
.await
.expect("Failed creating rpc client");

Expand Down
2 changes: 1 addition & 1 deletion rpc/tests/utils/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use jsonrpsee::core::client::SubscriptionClientT;
use jsonrpsee::core::ClientError;
use tokio::sync::{Mutex, MutexGuard};

const CELESTIA_RPC_URL: &str = "ws://localhost:26658";
const CELESTIA_RPC_URL: &str = "ws://localhost:36658";

async fn write_lock() -> MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
Expand Down
4 changes: 2 additions & 2 deletions rpc/tests/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use celestia_rpc::client::Client;
use celestia_rpc::prelude::*;
use wasm_bindgen_test::*;

// uses bridge-1, which has skip-auth enabled
const CELESTIA_RPC_URL: &str = "ws://localhost:36658";
// uses bridge-0, which has skip-auth enabled
const CELESTIA_RPC_URL: &str = "ws://localhost:26658";

wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

Expand Down
4 changes: 2 additions & 2 deletions tools/gen_auth_tokens.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ wait_for_docker_setup() {

# wait for the service to start
while :; do
curl http://127.0.0.1:26658 > /dev/null 2>&1 && break
curl http://127.0.0.1:36658 > /dev/null 2>&1 && break
sleep 1
done
}
Expand All @@ -41,7 +41,7 @@ ensure_dotenv_file() {

generate_token() {
local auth_level="$1"
docker compose -f "$DOCKER_COMPOSE_FILE" exec -T bridge-0 \
docker compose -f "$DOCKER_COMPOSE_FILE" exec -T bridge-1 \
celestia bridge auth "$auth_level" --p2p.network private
}

Expand Down
Loading