Skip to content

Commit

Permalink
ci(node-wasm): Add integration tests for node-wasm (#420)
Browse files Browse the repository at this point in the history

Signed-off-by: Mikołaj Florkiewicz <[email protected]>
Co-authored-by: Maciej Zwoliński <[email protected]>
  • Loading branch information
fl0rek and zvolin authored Nov 19, 2024
1 parent debcadf commit 38f0acf
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 20 deletions.
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
})
.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

0 comments on commit 38f0acf

Please sign in to comment.