From 3ffcdd200570fb402300d7f172ae2533429ffed9 Mon Sep 17 00:00:00 2001 From: magine Date: Thu, 28 Mar 2024 11:54:26 +0800 Subject: [PATCH] refactor: replace channel with callback of transport in swarm (#561) * Separate transport operation in swarm * Implement PayloadSender for SwarmTransport * Update handshake api * Update callback mode * Remove channel * Use MessageHandler in callback * Update rings-core api in rings-node * Simplify return type of gen_sorted_ht and gen_pure_dht * Implement a node in default test for monitoring * Fix static check of connection handler test * Fix static check of tests * Remove not using module and its dependencies * Fix static check of wasm tests * Fix callback of dummy transport * Should always invoke user defined callbacks * Should notify connected after data channel opened * Fix dummy test datachannel callback behaviour * Wait for msgs in stabilization and storage tests * Fix test_fourth_node_connection by incr wait time * Remove meaningless disconnected cleanup test * Fix len error in test_extend_data * Fix test_extend_data in storage handler * Test case enhance * Temeparary skip dummy tests * Wait for msg arriving in test * Fix handshake_on_both_sides test * Fix receiver dropping bug in test_stabilization_final_dht * Upgrade stabilization api * Hide the SwarmTransport in the Swarm of rings-core --- .github/workflows/qaci.yml | 4 +- Cargo.lock | 22 +- crates/core/Cargo.toml | 2 - crates/core/src/channels/default.rs | 63 -- crates/core/src/channels/mod.rs | 14 - crates/core/src/channels/wasm.rs | 67 -- crates/core/src/dht/chord.rs | 2 +- crates/core/src/dht/mod.rs | 3 +- crates/core/src/dht/stabilization.rs | 259 ++---- crates/core/src/inspect.rs | 17 +- crates/core/src/lib.rs | 2 - crates/core/src/measure.rs | 8 + .../core/src/message/handlers/connection.rs | 795 +++++------------- crates/core/src/message/handlers/custom.rs | 13 +- crates/core/src/message/handlers/dht.rs | 114 --- crates/core/src/message/handlers/mod.rs | 388 +++------ .../src/message/handlers/stabilization.rs | 477 ++--------- crates/core/src/message/handlers/storage.rs | 270 +++--- crates/core/src/message/handlers/subring.rs | 2 +- crates/core/src/message/mod.rs | 1 - crates/core/src/message/payload.rs | 1 + crates/core/src/message/types.rs | 18 - crates/core/src/swarm/builder.rs | 24 +- crates/core/src/swarm/callback.rs | 114 ++- crates/core/src/swarm/impls.rs | 451 ---------- crates/core/src/swarm/mod.rs | 435 +++------- crates/core/src/swarm/transport.rs | 345 ++++++++ crates/core/src/swarm/types.rs | 64 -- crates/core/src/tests/default/mod.rs | 160 +++- .../core/src/tests/default/test_connection.rs | 102 ++- .../src/tests/default/test_message_handler.rs | 478 +++++------ .../src/tests/default/test_stabilization.rs | 146 ++-- crates/core/src/tests/mod.rs | 27 +- crates/core/src/tests/wasm/mod.rs | 1 - crates/core/src/tests/wasm/test_channel.rs | 47 -- .../src/tests/wasm/test_wasm_transport.rs | 50 +- crates/core/src/types/channel.rs | 27 - crates/core/src/types/mod.rs | 20 - crates/node/bin/rings.rs | 8 +- crates/node/src/native/cli.rs | 2 - crates/node/src/native/config.rs | 12 +- crates/node/src/prelude.rs | 2 - crates/node/src/processor.rs | 166 ++-- crates/node/src/provider/browser/provider.rs | 127 +-- crates/node/src/provider/ffi.rs | 4 +- crates/node/src/provider/mod.rs | 4 +- crates/node/src/rpc_impl.rs | 49 +- crates/node/src/seed.rs | 12 +- crates/node/src/tests/native/mod.rs | 2 +- crates/node/src/tests/wasm/browser.rs | 14 +- crates/node/src/tests/wasm/mod.rs | 26 +- crates/node/src/tests/wasm/processor.rs | 102 +-- crates/rpc/src/protos/mod.rs | 19 +- crates/rpc/src/protos/rings_node.proto | 6 +- crates/rpc/src/protos/rings_node.rs | 9 +- crates/transport/Cargo.toml | 3 +- crates/transport/src/callback.rs | 7 +- crates/transport/src/connections/dummy/mod.rs | 22 +- .../src/connections/native_webrtc/mod.rs | 27 +- .../src/connections/web_sys_webrtc/mod.rs | 5 +- crates/transport/src/core/callback.rs | 5 + crates/transport/src/core/transport.rs | 20 - examples/ffi/rings.h | 2 +- examples/native/src/main.rs | 7 +- 64 files changed, 1868 insertions(+), 3827 deletions(-) delete mode 100644 crates/core/src/channels/default.rs delete mode 100644 crates/core/src/channels/mod.rs delete mode 100644 crates/core/src/channels/wasm.rs delete mode 100644 crates/core/src/message/handlers/dht.rs delete mode 100644 crates/core/src/swarm/impls.rs create mode 100644 crates/core/src/swarm/transport.rs delete mode 100644 crates/core/src/swarm/types.rs delete mode 100644 crates/core/src/tests/wasm/test_channel.rs delete mode 100644 crates/core/src/types/channel.rs delete mode 100644 crates/core/src/types/mod.rs diff --git a/.github/workflows/qaci.yml b/.github/workflows/qaci.yml index e486ad84e..24e01da8d 100644 --- a/.github/workflows/qaci.yml +++ b/.github/workflows/qaci.yml @@ -91,8 +91,8 @@ jobs: - name: Run doc tests run: cargo test --doc - - name: Run dummy tests - run: cargo test -p rings-core --features dummy --verbose + # - name: Run dummy tests + # run: cargo test -p rings-core --features dummy --verbose - name: Run tests run: cargo test --release --all --verbose diff --git a/Cargo.lock b/Cargo.lock index c5eeb991b..0e275890b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,17 +224,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "async-lock" version = "2.8.0" @@ -819,15 +808,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "concurrent-queue" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils 0.8.19", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -3788,7 +3768,6 @@ name = "rings-core" version = "0.5.6" dependencies = [ "arrayref", - "async-channel", "async-lock", "async-recursion", "async-stream", @@ -4007,6 +3986,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tokio-util", "tracing", "url", "wasm-bindgen", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index fee55b087..87b96ee8a 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,7 +18,6 @@ experimental = ["std"] default = ["std"] std = [ "webrtc", - "async-channel", "sled", "uuid/v4", "uuid/serde", @@ -88,7 +87,6 @@ tiny-keccak = { version = "2.0.1", features = ["keccak"] } uuid = { version = "0.8.2", optional = true } # default and dummy -async-channel = { version = "1.6.1", optional = true } sled = { version = "0.34.7", optional = true } webrtc = { workspace = true, optional = true } diff --git a/crates/core/src/channels/default.rs b/crates/core/src/channels/default.rs deleted file mode 100644 index 82147438c..000000000 --- a/crates/core/src/channels/default.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Default channel handle for `node` feature. -use async_channel as ac; -use async_channel::Receiver; -use async_channel::Sender; -use async_trait::async_trait; - -use crate::error::Error; -use crate::error::Result; -use crate::types::channel::Channel; - -/// Channel combine with async_channel::Sender and async_channel::Receiver. -#[derive(Debug)] -pub struct AcChannel { - /// async channel send `Message` through sender. - sender: Sender, - /// async channel rece `Message` through receiver. - receiver: Receiver, -} - -#[async_trait] -impl Channel for AcChannel -where T: std::fmt::Debug -{ - type Sender = Sender; - type Receiver = Receiver; - - fn new() -> Self { - let (tx, rx) = ac::unbounded(); - Self { - sender: tx, - receiver: rx, - } - } - - fn sender(&self) -> Self::Sender { - self.sender.clone() - } - - fn receiver(&self) -> Self::Receiver { - self.receiver.clone() - } - - async fn send(sender: &Self::Sender, msg: T) -> Result<()> { - tracing::debug!("channel sending message: {:?}", msg); - match sender.send(msg).await { - Ok(_) => { - tracing::debug!("channel send message success"); - Ok(()) - } - Err(_) => Err(Error::ChannelSendMessageFailed), - } - } - - async fn recv(receiver: &Self::Receiver) -> Result> { - match receiver.recv().await { - Ok(v) => { - tracing::debug!("channel received message: {:?}", v); - Ok(Some(v)) - } - Err(e) => Err(Error::ChannelRecvMessageFailed(e.to_string())), - } - } -} diff --git a/crates/core/src/channels/mod.rs b/crates/core/src/channels/mod.rs deleted file mode 100644 index 5f1489ab0..000000000 --- a/crates/core/src/channels/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Async channel for both browser(wasm) and native(node). - -#[cfg(not(feature = "wasm"))] -mod default; -#[cfg(feature = "wasm")] -mod wasm; - -/// A Channel build with wasm feature or not. -/// - wasm features: using mpsc::UnboundedSender and mpsc::UnboundedReceiver. -/// - default fatures: using async_channel::Sender and async_channel::Receiver. -#[cfg(not(feature = "wasm"))] -pub use default::AcChannel as Channel; -#[cfg(feature = "wasm")] -pub use wasm::CbChannel as Channel; diff --git a/crates/core/src/channels/wasm.rs b/crates/core/src/channels/wasm.rs deleted file mode 100644 index e003870eb..000000000 --- a/crates/core/src/channels/wasm.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Async channel handle for `wasm` features. -use std::sync::Arc; - -/// ref: https://github.com/Ciantic/rust-shared-wasm-experiments/blob/master/src/lib.rs -use async_trait::async_trait; -use futures::channel::mpsc; -use futures::lock::Mutex; - -use crate::error::Error; -use crate::error::Result; -use crate::types::channel::Channel; - -type Sender = Arc>; -type Receiver = Arc>>; - -/// Channel combine with mpsc::UnboundedSender and mpsc::UnboundedReceiver. -#[derive(Debug)] -pub struct CbChannel { - sender: Sender, - receiver: Receiver, -} - -#[async_trait(?Send)] -impl Channel for CbChannel { - type Sender = Sender; - type Receiver = Receiver; - - fn new() -> Self { - let (tx, rx) = mpsc::unbounded(); - Self { - sender: Arc::new(tx), - receiver: Arc::new(Mutex::new(rx)), - } - } - - fn sender(&self) -> Self::Sender { - self.sender.clone() - } - - fn receiver(&self) -> Self::Receiver { - self.receiver.clone() - } - - /// Sends a message along this channel. - /// This is an unbounded sender, so this function differs from Sink::send - /// by ensuring the return type reflects that the channel is always ready to receive messages. - /// Err(TrySendError) can caused by `is_full` or `is_disconnected` - /// ref: https://docs.rs/futures/latest/futures/channel/mpsc/struct.UnboundedSender.html - async fn send(sender: &Self::Sender, msg: T) -> Result<()> { - match sender.unbounded_send(msg) { - Ok(()) => Ok(()), - Err(_) => Err(Error::ChannelSendMessageFailed), - } - } - - async fn recv(receiver: &Self::Receiver) -> Result> { - let mut receiver = receiver.lock().await; - match receiver.try_next() { - // when there are no messages available, but channel is not yet closed - Err(_) => Ok(None), - // when message is fetched - Ok(Some(x)) => Ok(Some(x)), - // when channel is closed and no messages left in the queue - Ok(None) => Ok(None), - } - } -} diff --git a/crates/core/src/dht/chord.rs b/crates/core/src/dht/chord.rs index 7db1c8472..fdb3009b0 100644 --- a/crates/core/src/dht/chord.rs +++ b/crates/core/src/dht/chord.rs @@ -903,7 +903,7 @@ mod tests { } } - let dhts = gen_sorted_dht(5).await; + let dhts = gen_sorted_dht(5); let [n1, n2, n3, n4, n5] = dhts.as_slice() else { panic!("wrong dhts length"); }; diff --git a/crates/core/src/dht/mod.rs b/crates/core/src/dht/mod.rs index 3506bc185..c39ab95a6 100644 --- a/crates/core/src/dht/mod.rs +++ b/crates/core/src/dht/mod.rs @@ -22,8 +22,7 @@ pub use chord::TopoInfo; pub use chord::VNodeStorage; pub use did::Did; pub use finger::FingerTable; -pub use stabilization::Stabilization; -pub use stabilization::TStabilize; +pub use stabilization::Stabilizer; pub use successor::SuccessorReader; pub use successor::SuccessorWriter; pub use types::Chord; diff --git a/crates/core/src/dht/stabilization.rs b/crates/core/src/dht/stabilization.rs index bc8d77a0c..7c7673242 100644 --- a/crates/core/src/dht/stabilization.rs +++ b/crates/core/src/dht/stabilization.rs @@ -1,8 +1,8 @@ -//! Stabilization wait to notify predecessors and update fingersTable. +//! Stabilization run daemons to maintain dht. + use std::sync::Arc; -use async_trait::async_trait; -use rings_transport::core::transport::ConnectionInterface; +use rings_transport::core::transport::WebrtcConnectionState; use crate::dht::successor::SuccessorReader; use crate::dht::types::CorrectChord; @@ -11,7 +11,6 @@ use crate::dht::PeerRing; use crate::dht::PeerRingAction; use crate::dht::PeerRingRemoteAction; use crate::error::Result; -use crate::message::handlers::MessageHandlerEvent; use crate::message::FindSuccessorReportHandler; use crate::message::FindSuccessorSend; use crate::message::FindSuccessorThen; @@ -20,79 +19,90 @@ use crate::message::MessagePayload; use crate::message::NotifyPredecessorSend; use crate::message::PayloadSender; use crate::message::QueryForTopoInfoSend; -use crate::swarm::Swarm; +use crate::swarm::transport::SwarmTransport; -/// A combination contains chord and swarm, use to run stabilize. -/// - swarm: transports communicate with each others. -/// - chord: fix local fingers table. +/// The stabilization runner. #[derive(Clone)] -pub struct Stabilization { - chord: Arc, - swarm: Arc, - timeout: u64, +pub struct Stabilizer { + transport: Arc, + dht: Arc, } -/// A trait with `wait` method. -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait TStabilize { - /// Wait and poll - async fn wait(self: Arc); -} +impl Stabilizer { + /// Create a new stabilization runner. + pub fn new(transport: Arc) -> Self { + let dht = transport.dht.clone(); + Self { transport, dht } + } + + /// Run stabilization once. + pub async fn stabilize(&self) -> Result<()> { + tracing::debug!("STABILIZATION notify_predecessor start"); + if let Err(e) = self.notify_predecessor().await { + tracing::error!("[stabilize] Failed on notify predecessor {:?}", e); + } + tracing::debug!("STABILIZATION notify_predecessor end"); + tracing::debug!("STABILIZATION fix_fingers start"); + if let Err(e) = self.fix_fingers().await { + tracing::error!("[stabilize] Failed on fix_finger {:?}", e); + } + tracing::debug!("STABILIZATION fix_fingers end"); + tracing::debug!("STABILIZATION clean_unavailable_connections start"); + if let Err(e) = self.clean_unavailable_connections().await { + tracing::error!( + "[stabilize] Failed on clean unavailable connections {:?}", + e + ); + } + tracing::debug!("STABILIZATION clean_unavailable_connections end"); + #[cfg(feature = "experimental")] + { + tracing::debug!("STABILIZATION correct_stabilize start"); + if let Err(e) = self.correct_stabilize().await { + tracing::error!("[stabilize] Failed on call correct stabilize {:?}", e); + } + tracing::debug!("STABILIZATION correct_stabilize end"); + } + Ok(()) + } -impl Stabilization { /// Clean unavailable connections in transport. pub async fn clean_unavailable_connections(&self) -> Result<()> { - let conns = self.swarm.get_connections(); + let conns = self.transport.get_connections(); for (did, conn) in conns.into_iter() { - if conn.is_disconnected().await { + if matches!( + conn.webrtc_connection_state(), + WebrtcConnectionState::Disconnected + | WebrtcConnectionState::Failed + | WebrtcConnectionState::Closed + ) { tracing::info!("STABILIZATION clean_unavailable_transports: {:?}", did); - self.swarm.disconnect(did).await?; + self.transport.disconnect(did).await?; } } Ok(()) } -} -impl Stabilization { - /// Create a new instance of Stabilization - pub fn new(swarm: Arc, timeout: u64) -> Self { - Self { - chord: swarm.dht(), - swarm, - timeout, - } - } - - /// Get timeout of waiting delays. - pub fn get_timeout(&self) -> u64 { - self.timeout - } -} - -impl Stabilization { /// Notify predecessor, this is a DHT operation. pub async fn notify_predecessor(&self) -> Result<()> { let (successor_min, successor_list) = { - let successor = self.chord.successors(); + let successor = self.dht.successors(); (successor.min()?, successor.list()?) }; - let msg = Message::NotifyPredecessorSend(NotifyPredecessorSend { - did: self.chord.did, - }); - if self.chord.did != successor_min { + let msg = Message::NotifyPredecessorSend(NotifyPredecessorSend { did: self.dht.did }); + if self.dht.did != successor_min { for s in successor_list { tracing::debug!("STABILIZATION notify_predecessor: {:?}", s); let payload = MessagePayload::new_send( msg.clone(), - self.swarm.session_sk(), + self.transport.session_sk(), s, - self.swarm.did(), + self.dht.did, )?; - self.swarm.send_payload(payload).await?; + self.transport.send_payload(payload).await?; } Ok(()) } else { @@ -102,7 +112,7 @@ impl Stabilization { /// Fix fingers from finger table, this is a DHT operation. async fn fix_fingers(&self) -> Result<()> { - match self.chord.fix_fingers() { + match self.dht.fix_fingers() { Ok(action) => match action { PeerRingAction::None => Ok(()), PeerRingAction::RemoteAction( @@ -117,11 +127,11 @@ impl Stabilization { }); let payload = MessagePayload::new_send( msg.clone(), - self.swarm.session_sk(), + self.transport.session_sk(), closest_predecessor, closest_predecessor, )?; - self.swarm.send_payload(payload).await?; + self.transport.send_payload(payload).await?; Ok(()) } _ => { @@ -135,54 +145,20 @@ impl Stabilization { } } } -} -impl Stabilization { /// Call stabilization from correct chord implementation pub async fn correct_stabilize(&self) -> Result<()> { if let PeerRingAction::RemoteAction( next, PeerRingRemoteAction::QueryForSuccessorListAndPred, - ) = self.chord.pre_stabilize()? + ) = self.dht.pre_stabilize()? { - let evs = vec![MessageHandlerEvent::SendDirectMessage( - Message::QueryForTopoInfoSend(QueryForTopoInfoSend::new_for_stab(next)), - next, - )]; - return self.swarm.handle_message_handler_events(&evs).await; - } - Ok(()) - } -} - -impl Stabilization { - /// Call stabilize periodly. - pub async fn stabilize(&self) -> Result<()> { - tracing::debug!("STABILIZATION notify_predecessor start"); - if let Err(e) = self.notify_predecessor().await { - tracing::error!("[stabilize] Failed on notify predecessor {:?}", e); - } - tracing::debug!("STABILIZATION notify_predecessor end"); - tracing::debug!("STABILIZATION fix_fingers start"); - if let Err(e) = self.fix_fingers().await { - tracing::error!("[stabilize] Failed on fix_finger {:?}", e); - } - tracing::debug!("STABILIZATION fix_fingers end"); - tracing::debug!("STABILIZATION clean_unavailable_connections start"); - if let Err(e) = self.clean_unavailable_connections().await { - tracing::error!( - "[stabilize] Failed on clean unavailable connections {:?}", - e - ); - } - tracing::debug!("STABILIZATION clean_unavailable_connections end"); - #[cfg(feature = "experimental")] - { - tracing::debug!("STABILIZATION correct_stabilize start"); - if let Err(e) = self.correct_stabilize().await { - tracing::error!("[stabilize] Failed on call correct stabilize {:?}", e); - } - tracing::debug!("STABILIZATION correct_stabilize end"); + self.transport + .send_direct_message( + Message::QueryForTopoInfoSend(QueryForTopoInfoSend::new_for_stab(next)), + next, + ) + .await?; } Ok(()) } @@ -193,20 +169,18 @@ mod stabilizer { use std::sync::Arc; use std::time::Duration; - use async_trait::async_trait; use futures::future::FutureExt; use futures::pin_mut; use futures::select; use futures_timer::Delay; - use super::Stabilization; - use super::TStabilize; + use super::*; - #[async_trait] - impl TStabilize for Stabilization { - async fn wait(self: Arc) { + impl Stabilizer { + /// Run stabilization in a loop. + pub async fn wait(self: Arc, interval: Duration) { loop { - let timeout = Delay::new(Duration::from_secs(self.timeout)).fuse(); + let timeout = Delay::new(interval).fuse(); pin_mut!(timeout); select! { _ = timeout => self @@ -222,19 +196,17 @@ mod stabilizer { #[cfg(feature = "wasm")] mod stabilizer { use std::sync::Arc; + use std::time::Duration; - use async_trait::async_trait; use wasm_bindgen_futures::spawn_local; - use super::Stabilization; - use super::TStabilize; + use super::*; use crate::poll; - #[async_trait(?Send)] - impl TStabilize for Stabilization { - async fn wait(self: Arc) { + impl Stabilizer { + /// Run stabilization in a loop. + pub async fn wait(self: Arc, interval: Duration) { let caller = Arc::clone(&self); - let timeout = caller.timeout; let func = move || { let caller = caller.clone(); spawn_local(Box::pin(async move { @@ -244,78 +216,7 @@ mod stabilizer { .unwrap_or_else(|e| tracing::error!("failed to stabilize {:?}", e)); })) }; - poll!(func, (timeout * 1000).try_into().unwrap()); + poll!(func, interval.as_millis().try_into().unwrap()); } } } - -#[cfg(not(any(feature = "wasm", feature = "dummy")))] -#[cfg(test)] -pub mod tests { - use std::time::Duration; - - use rings_transport::core::transport::WebrtcConnectionState; - - use super::*; - use crate::ecc::SecretKey; - use crate::tests::default::prepare_node; - use crate::tests::manually_establish_connection; - - #[tokio::test] - async fn test_clean_unavailable_connections() { - let key1 = SecretKey::random(); - let key2 = SecretKey::random(); - let key3 = SecretKey::random(); - let node1 = prepare_node(key1).await; - let node2 = prepare_node(key2).await; - let node3 = prepare_node(key3).await; - - // Shouldn't listen to message handler here, - // otherwise it will automatically remove disconnected transport. - - manually_establish_connection(&node1, &node2).await; - manually_establish_connection(&node1, &node3).await; - - tokio::time::sleep(Duration::from_secs(5)).await; - - assert_eq!( - node1 - .get_connection(node2.did()) - .unwrap() - .webrtc_connection_state(), - WebrtcConnectionState::Connected, - ); - assert_eq!( - node1 - .get_connection(node3.did()) - .unwrap() - .webrtc_connection_state(), - WebrtcConnectionState::Connected, - ); - - node2.disconnect(node1.did()).await.unwrap(); - node3.disconnect(node1.did()).await.unwrap(); - - tokio::time::sleep(Duration::from_secs(10)).await; - assert_eq!( - node1 - .get_connection(node2.did()) - .unwrap() - .webrtc_connection_state(), - WebrtcConnectionState::Disconnected, - ); - assert_eq!( - node1 - .get_connection(node3.did()) - .unwrap() - .webrtc_connection_state(), - WebrtcConnectionState::Disconnected, - ); - - let stb = Stabilization::new(node1.clone(), 3); - stb.clean_unavailable_connections().await.unwrap(); - - assert!(node1.get_connection(node2.did()).is_none()); - assert!(node1.get_connection(node3.did()).is_none()); - } -} diff --git a/crates/core/src/inspect.rs b/crates/core/src/inspect.rs index 89d00582c..51789f722 100644 --- a/crates/core/src/inspect.rs +++ b/crates/core/src/inspect.rs @@ -1,4 +1,3 @@ -use rings_transport::core::transport::ConnectionInterface; use serde::Deserialize; use serde::Serialize; @@ -10,7 +9,7 @@ use crate::swarm::Swarm; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SwarmInspect { - pub connections: Vec, + pub peers: Vec, pub dht: DHTInspect, pub persistence_storage: StorageInspect, pub cache_storage: StorageInspect, @@ -39,22 +38,12 @@ pub struct StorageInspect { impl SwarmInspect { pub async fn inspect(swarm: &Swarm) -> Self { let dht = DHTInspect::inspect(&swarm.dht()); - let connections = { - let connections = swarm.get_connections(); - - connections - .iter() - .map(|(did, c)| ConnectionInspect { - did: did.to_string(), - state: format!("{:?}", c.ice_connection_state()), - }) - .collect() - }; + let peers = swarm.peers(); let persistence_storage = StorageInspect::inspect_kv_storage(&swarm.dht().storage).await; let cache_storage = StorageInspect::inspect_kv_storage(&swarm.dht().cache).await; Self { - connections, + peers, dht, persistence_storage, cache_storage, diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 2ab17e6e8..a5d8bed40 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -60,7 +60,6 @@ //! ``` #![cfg_attr(target_arch = "wasm32", allow(clippy::arc_with_non_send_sync))] -pub mod channels; pub mod dht; pub mod ecc; pub mod error; @@ -72,7 +71,6 @@ pub mod storage; pub mod swarm; #[cfg(test)] mod tests; -pub mod types; pub mod utils; pub use async_trait::async_trait; pub use futures; diff --git a/crates/core/src/measure.rs b/crates/core/src/measure.rs index 905094adb..7089aeb2e 100644 --- a/crates/core/src/measure.rs +++ b/crates/core/src/measure.rs @@ -5,6 +5,14 @@ use async_trait::async_trait; use crate::dht::Did; +/// Type of Measure, see [Measure]. +#[cfg(not(feature = "wasm"))] +pub type MeasureImpl = Box; + +/// Type of Measure, see [crate::measure::Measure]. +#[cfg(feature = "wasm")] +pub type MeasureImpl = Box; + /// The tag of counters in measure. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MeasureCounter { diff --git a/crates/core/src/message/handlers/connection.rs b/crates/core/src/message/handlers/connection.rs index 695cf021a..5bce231db 100644 --- a/crates/core/src/message/handlers/connection.rs +++ b/crates/core/src/message/handlers/connection.rs @@ -1,10 +1,7 @@ -use std::ops::Deref; - use async_trait::async_trait; -use super::dht; +use crate::dht::types::Chord; use crate::dht::types::CorrectChord; -use crate::dht::Chord; use crate::dht::PeerRingAction; use crate::dht::TopoInfo; use crate::error::Error; @@ -13,7 +10,6 @@ use crate::message::types::ConnectNodeReport; use crate::message::types::ConnectNodeSend; use crate::message::types::FindSuccessorReport; use crate::message::types::FindSuccessorSend; -use crate::message::types::JoinDHT; use crate::message::types::Message; use crate::message::types::QueryForTopoInfoReport; use crate::message::types::QueryForTopoInfoSend; @@ -21,29 +17,22 @@ use crate::message::types::Then; use crate::message::FindSuccessorReportHandler; use crate::message::FindSuccessorThen; use crate::message::HandleMsg; -use crate::message::LeaveDHT; use crate::message::MessageHandler; -use crate::message::MessageHandlerEvent; use crate::message::MessagePayload; +use crate::message::PayloadSender; /// QueryForTopoInfoSend is direct message #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &QueryForTopoInfoSend, - ) -> Result> { - let info: TopoInfo = TopoInfo::try_from(self.dht.deref())?; + async fn handle(&self, ctx: &MessagePayload, msg: &QueryForTopoInfoSend) -> Result<()> { + let info: TopoInfo = TopoInfo::try_from(self.dht.as_ref())?; if msg.did == self.dht.did { - Ok(vec![MessageHandlerEvent::SendReportMessage( - ctx.clone(), - Message::QueryForTopoInfoReport(msg.resp(info)), - )]) - } else { - Ok(vec![]) + self.transport + .send_report_message(ctx, Message::QueryForTopoInfoReport(msg.resp(info))) + .await? } + Ok(()) } } @@ -51,70 +40,36 @@ impl HandleMsg for MessageHandler { #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &QueryForTopoInfoReport, - ) -> Result> { + async fn handle(&self, _ctx: &MessagePayload, msg: &QueryForTopoInfoReport) -> Result<()> { match msg.then { - ::Then::SyncSuccessor => Ok(msg - .info - .successors - .iter() - .map(|did| MessageHandlerEvent::JoinDHT(ctx.clone(), *did)) - .collect()), + ::Then::SyncSuccessor => { + for peer in msg.info.successors.iter() { + self.join_dht(*peer).await?; + } + } ::Then::Stabilization => { let ev = self.dht.stabilize(msg.info.clone())?; - dht::handle_dht_events(&ev, ctx).await + self.handle_dht_events(&ev).await?; } } - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl HandleMsg for MessageHandler { - async fn handle( - &self, - _ctx: &MessagePayload, - msg: &LeaveDHT, - ) -> Result> { - Ok(vec![MessageHandlerEvent::LeaveDHT(msg.did)]) - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &JoinDHT, - ) -> Result> { - // here is two situation. - // finger table just have no other node(beside next), it will be a `create` op - // otherwise, it will be a `send` op - // let act = self.dht.join(msg.did)?; - // handle_join_dht(&self, act, ctx).await - Ok(vec![MessageHandlerEvent::JoinDHT(ctx.clone(), msg.did)]) + Ok(()) } } #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &ConnectNodeSend, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &ConnectNodeSend) -> Result<()> { if self.dht.did != ctx.relay.destination { - Ok(vec![MessageHandlerEvent::ForwardPayload(ctx.clone(), None)]) + self.transport.forward_payload(ctx, None).await } else { - Ok(vec![MessageHandlerEvent::AnswerOffer( - ctx.clone(), - msg.clone(), - )]) + let answer = self + .transport + .answer_remote_connection(ctx.relay.origin_sender(), self.inner_callback(), msg) + .await?; + self.transport + .send_report_message(ctx, Message::ConnectNodeReport(answer)) + .await } } } @@ -122,18 +77,13 @@ impl HandleMsg for MessageHandler { #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &ConnectNodeReport, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &ConnectNodeReport) -> Result<()> { if self.dht.did != ctx.relay.destination { - Ok(vec![MessageHandlerEvent::ForwardPayload(ctx.clone(), None)]) + self.transport.forward_payload(ctx, None).await } else { - Ok(vec![MessageHandlerEvent::AcceptAnswer( - ctx.relay.origin_sender(), - msg.clone(), - )]) + self.transport + .accept_remote_connection(ctx.relay.origin_sender(), msg) + .await } } } @@ -141,37 +91,29 @@ impl HandleMsg for MessageHandler { #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &FindSuccessorSend, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &FindSuccessorSend) -> Result<()> { match self.dht.find_successor(msg.did)? { PeerRingAction::Some(did) => { if !msg.strict || self.dht.did == msg.did { match &msg.then { FindSuccessorThen::Report(handler) => { - Ok(vec![MessageHandlerEvent::SendReportMessage( - ctx.clone(), - Message::FindSuccessorReport(FindSuccessorReport { - did, - handler: handler.clone(), - }), - )]) + self.transport + .send_report_message( + ctx, + Message::FindSuccessorReport(FindSuccessorReport { + did, + handler: handler.clone(), + }), + ) + .await } } } else { - Ok(vec![MessageHandlerEvent::ForwardPayload( - ctx.clone(), - Some(did), - )]) + self.transport.forward_payload(ctx, Some(did)).await } } PeerRingAction::RemoteAction(next, _) => { - Ok(vec![MessageHandlerEvent::ResetDestination( - ctx.clone(), - next, - )]) + self.transport.reset_destination(ctx, next).await } act => Err(Error::PeerRingUnexpectedAction(act)), } @@ -181,22 +123,27 @@ impl HandleMsg for MessageHandler { #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &FindSuccessorReport, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &FindSuccessorReport) -> Result<()> { if self.dht.did != ctx.relay.destination { - return Ok(vec![MessageHandlerEvent::ForwardPayload(ctx.clone(), None)]); + return self.transport.forward_payload(ctx, None).await; } match &msg.handler { - FindSuccessorReportHandler::FixFingerTable => { - Ok(vec![MessageHandlerEvent::Connect(msg.did)]) + FindSuccessorReportHandler::FixFingerTable | FindSuccessorReportHandler::Connect => { + if msg.did != self.dht.did { + let offer_msg = self + .transport + .prepare_connection_offer(msg.did, self.inner_callback()) + .await?; + self.transport + .send_message(Message::ConnectNodeSend(offer_msg), msg.did) + .await?; + } } - FindSuccessorReportHandler::Connect => Ok(vec![MessageHandlerEvent::Connect(msg.did)]), - _ => Ok(vec![]), + _ => {} } + + Ok(()) } } @@ -204,21 +151,19 @@ impl HandleMsg for MessageHandler { #[cfg(test)] pub mod tests { use std::matches; - use std::sync::Arc; - use rings_transport::core::transport::ConnectionInterface; + use rings_transport::core::transport::WebrtcConnectionState; use tokio::time::sleep; use tokio::time::Duration; use super::*; use crate::dht::successor::SuccessorReader; - use crate::dht::Did; use crate::ecc::tests::gen_ordered_keys; use crate::ecc::SecretKey; - use crate::message::handlers::tests::assert_no_more_msg; - use crate::message::MessageVerificationExt; - use crate::swarm::Swarm; + use crate::tests::default::assert_no_more_msg; use crate::tests::default::prepare_node; + use crate::tests::default::wait_for_msgs; + use crate::tests::default::Node; use crate::tests::manually_establish_connection; // node1.key < node2.key < node3.key @@ -337,7 +282,7 @@ pub mod tests { key1: SecretKey, key2: SecretKey, key3: SecretKey, - ) -> Result<(Arc, Arc, Arc)> { + ) -> Result<(Node, Node, Node)> { let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; let node3 = prepare_node(key3).await; @@ -346,8 +291,13 @@ pub mod tests { println!("|| now we connect node1 and node2 ||"); println!("========================================"); - test_only_two_nodes_establish_connection(&node1, &node2).await?; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; + node1.assert_transports(vec![node2.did()]); + node2.assert_transports(vec![node1.did()]); + node3.assert_transports(vec![]); assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); assert_eq!(node2.dht().successors().list()?, vec![node1.did()]); assert_eq!(node3.dht().successors().list()?, vec![]); @@ -356,57 +306,15 @@ pub mod tests { println!("|| now we start join node3 to node2 ||"); println!("========================================"); - manually_establish_connection(&node3, &node2).await; - test_listen_join_and_init_find_succeesor(&node3, &node2).await?; - - assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); - assert_eq!(node2.dht().successors().list()?, vec![ - node3.did(), - node1.did() - ]); - assert_eq!(node3.dht().successors().list()?, vec![node2.did()]); - - // 2->3 FindSuccessorReport - // node2 report node3 as node3's successor to node3 - // - // node2 think there is no did that is closer to node3 than node3, so it respond node3 - // to understand that you can imagine the layouts on the clock circle: - // - // node2 -> node3 -> node1 - // ^ | - // |-----------------| - // - // as you can see, in node2's view, node1 is farther than node3 - // so node2 pick node3 as node3's successor - // - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node2.did()); - assert_eq!(ev_3.relay.path, vec![node2.did()]); - assert!(matches!( - ev_3.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node3.did() - )); - // dht3 won't set did3 as successor - assert!(!node3.dht().successors().list()?.contains(&node3.did())); - - // 3->2 FindSuccessorReport - // node3 report node2 as node2's successor to node2 - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node3.did()); - assert_eq!(ev_2.relay.path, vec![node3.did()]); - // node3 is only aware of node2, so it respond node2 - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node2.did() - )); - // dht2 won't set did2 as successor - assert!(!node2.dht().successors().list()?.contains(&node2.did())); + manually_establish_connection(&node3.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state before connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did()]); - assert_transports(node2.clone(), vec![node1.did(), node3.did()]); - assert_transports(node3.clone(), vec![node2.did()]); - assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); + node1.assert_transports(vec![node2.did()]); + node2.assert_transports(vec![node1.did(), node3.did()]); + node3.assert_transports(vec![node2.did()]); + assert_eq!(node1.dht().successors().list()?, vec![node2.did(),]); assert_eq!(node2.dht().successors().list()?, vec![ node3.did(), node1.did() @@ -417,53 +325,19 @@ pub mod tests { println!("|| now we connect node1 to node3 via DHT ||"); println!("============================================="); - test_connect_via_dht_and_init_find_succeesor(&node1, &node2, &node3).await?; - - // The following are other communications after successful connection - - // 3->1->2 FindSuccessorSend - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node1.did()); - assert_eq!(ev_2.relay.path, vec![node3.did(), node1.did()]); - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, strict: false, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect)}) if did == node3.did() - )); - - // 3->1 FindSuccessorReport - // node3 report node1 as node1's successor to node1 - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node3.did()); - assert_eq!(ev_1.relay.path, vec![node3.did()]); - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node1.did() - )); - // dht1 won't set did1 as successor - assert!(!node1.dht().successors().list()?.contains(&node1.did())); - - // 2->1 FindSuccessorReport - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node2.did()); - assert_eq!(ev_1.relay.path, vec![node2.did()]); - - // 2->1->3 FindSuccessorReport - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node1.did()); - assert_eq!(ev_3.relay.path, vec![node2.did(), node1.did()]); - assert!(matches!( - ev_3.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node3.did() - )); - // dht3 won't set did3 as successor - assert!(!node3.dht().successors().list()?.contains(&node3.did())); + // check node1 and node3 is not connected to each other + assert!(node1.swarm.transport.get_connection(node3.did()).is_none()); + // node1's successor should be node2 now + assert_eq!(node1.dht().successors().max()?, node2.did()); + node1.swarm.connect(node3.did()).await.unwrap(); + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did(), node3.did()]); - assert_transports(node2.clone(), vec![node1.did(), node3.did()]); - assert_transports(node3.clone(), vec![node1.did(), node2.did()]); + node1.assert_transports(vec![node2.did(), node3.did()]); + node2.assert_transports(vec![node1.did(), node3.did()]); + node3.assert_transports(vec![node1.did(), node2.did()]); assert_eq!(node1.dht().successors().list()?, vec![ node2.did(), node3.did() @@ -476,14 +350,15 @@ pub mod tests { node1.did(), node2.did() ]); - Ok((node1.clone(), node2.clone(), node3.clone())) + + Ok((node1, node2, node3)) } async fn test_triple_desc_ordered_nodes_connection( key1: SecretKey, key2: SecretKey, key3: SecretKey, - ) -> Result<(Arc, Arc, Arc)> { + ) -> Result<(Node, Node, Node)> { let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; let node3 = prepare_node(key3).await; @@ -492,7 +367,9 @@ pub mod tests { println!("|| now we connect node1 and node2 ||"); println!("========================================"); - test_only_two_nodes_establish_connection(&node1, &node2).await?; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); assert_eq!(node2.dht().successors().list()?, vec![node1.did()]); @@ -502,74 +379,14 @@ pub mod tests { println!("|| now we start join node3 to node2 ||"); println!("========================================"); - manually_establish_connection(&node3, &node2).await; - test_listen_join_and_init_find_succeesor(&node3, &node2).await?; - - assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); - assert_eq!(node2.dht().successors().list()?, vec![ - node1.did(), - node3.did() - ]); - assert_eq!(node3.dht().successors().list()?, vec![node2.did()]); - - // 3->2->1 FindSuccessorSend - // node2 think node1 is closer than itself to node3, so it relay msg to node1 - // - // to understand that you can imagine the layouts on the clock circle: - // - // node2 -> node1 -> node3 - // ^ | - // |-----------------| - // - // as you can see, in node2's view, node1 is closer than node2 to node3. - // so node2 pick node1 to find_successor. - // - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node2.did()); - assert_eq!(ev_1.relay.path, vec![node3.did(), node2.did()]); - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, strict: false, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect)}) if did == node3.did() - )); - - // 3->2 FindSuccessorReport - // node3 report node2 as node2's successor to node2 - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node3.did()); - assert_eq!(ev_2.relay.path, vec![node3.did()]); - // node3 is only aware of node2, so it respond node2 - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node2.did() - )); - // dht2 won't set did2 as successor - assert!(!node2.dht().successors().list()?.contains(&node2.did())); - - // 1->2 FindSuccessorReport - // node1 report node2 as node3's successor to node2 - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node1.did()); - assert_eq!(ev_2.relay.path, vec![node1.did()]); - // node1 is only aware of node2, so it respond node2 - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node2.did() - )); - - // 1->2->3 FindSuccessorReport - // node2 relay report to node3 - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node2.did()); - assert_eq!(ev_3.relay.path, vec![node1.did(), node2.did()]); - assert!(matches!( - ev_3.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node2.did() - )); + manually_establish_connection(&node3.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state before connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did()]); - assert_transports(node2.clone(), vec![node1.did(), node3.did()]); - assert_transports(node3.clone(), vec![node2.did()]); + node1.assert_transports(vec![node2.did()]); + node2.assert_transports(vec![node1.did(), node3.did()]); + node3.assert_transports(vec![node2.did()]); assert_eq!(node1.dht().successors().list()?, vec![node2.did()]); assert_eq!(node2.dht().successors().list()?, vec![ node1.did(), @@ -581,53 +398,19 @@ pub mod tests { println!("|| now we connect node1 to node3 via DHT ||"); println!("============================================="); - test_connect_via_dht_and_init_find_succeesor(&node1, &node2, &node3).await?; - - // The following are other communications after successful connection - - // 1->3->2 FindSuccessorSend - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node3.did()); - assert_eq!(ev_2.relay.path, vec![node1.did(), node3.did()]); - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, strict: false, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect)}) if did == node1.did() - )); - - // 1->3 FindSuccessorReport - // node1 report node3 as node3's successor to node1 - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node1.did()); - assert_eq!(ev_3.relay.path, vec![node1.did()]); - assert!(matches!( - ev_3.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node3.did() - )); - // dht3 won't set did3 as successor - assert!(!node3.dht.successors().list()?.contains(&node3.did())); - - // 2->3 FindSuccessorReport - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node2.did()); - assert_eq!(ev_3.relay.path, vec![node2.did()]); - - // 2->3->1 FindSuccessorReport - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node3.did()); - assert_eq!(ev_1.relay.path, vec![node2.did(), node3.did()]); - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node1.did() - )); - // dht1 won't set did1 as successor - assert!(!node1.dht.successors().list()?.contains(&node1.did())); + // check node1 and node3 is not connected to each other + assert!(node1.swarm.transport.get_connection(node3.did()).is_none()); + // node1's successor should be node2 now + assert_eq!(node1.dht().successors().max()?, node2.did()); + node1.swarm.connect(node3.did()).await.unwrap(); + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did(), node3.did()]); - assert_transports(node2.clone(), vec![node1.did(), node3.did()]); - assert_transports(node3.clone(), vec![node1.did(), node2.did()]); + node1.assert_transports(vec![node2.did(), node3.did()]); + node2.assert_transports(vec![node1.did(), node3.did()]); + node3.assert_transports(vec![node1.did(), node2.did()]); assert_eq!(node1.dht().successors().list()?, vec![ node3.did(), node2.did() @@ -641,182 +424,7 @@ pub mod tests { node1.did() ]); - Ok((node1.clone(), node2.clone(), node3.clone())) - } - - pub async fn test_listen_join_and_init_find_succeesor( - node1: &Swarm, - node2: &Swarm, - ) -> Result<()> { - // 1 JoinDHT - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node1.did()); - assert_eq!(ev_1.relay.path, vec![node1.did()]); - assert!( - matches!(ev_1.transaction.data()?, Message::JoinDHT(JoinDHT{did, ..}) if did == node2.did()) - ); - // 2 JoinDHT - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node2.did()); - assert_eq!(ev_2.relay.path, vec![node2.did()]); - assert!( - matches!(ev_2.transaction.data()?, Message::JoinDHT(JoinDHT{did, ..}) if did == node1.did()) - ); - // 1->2 FindSuccessorSend - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node2.did()); - assert_eq!(ev_1.relay.path, vec![node2.did()]); - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), strict: false}) if did == node2.did() - )); - // 2->1 FindSuccessorSend - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node1.did()); - assert_eq!(ev_2.relay.path, vec![node1.did()]); - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), strict: false}) if did == node1.did() - )); - - Ok(()) - } - - pub async fn test_only_two_nodes_establish_connection( - node1: &Swarm, - node2: &Swarm, - ) -> Result<()> { - manually_establish_connection(node1, node2).await; - test_listen_join_and_init_find_succeesor(node1, node2).await?; - - // 2->1 FindSuccessorReport - // node2 report node1 as node1's successor to node1 - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node2.did()); - assert_eq!(ev_1.relay.path, vec![node2.did()]); - // node2 is only aware of node1, so it respond node1 - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node1.did() - )); - // dht1 won't set dhd1 as successor - assert!(!node1.dht().successors().list()?.contains(&node1.did())); - - // 1->2 FindSuccessorReport - // node1 report node2 as node2's successor to node2 - let ev_2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev_2.signer(), node1.did()); - assert_eq!(ev_2.relay.path, vec![node1.did()]); - // node1 is only aware of node2, so it respond node2 - assert!(matches!( - ev_2.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport{did, handler: FindSuccessorReportHandler::Connect}) if did == node2.did() - )); - // dht2 won't set did2 as successor - assert!(!node2.dht().successors().list()?.contains(&node2.did())); - - Ok(()) - } - - async fn test_connect_via_dht_and_init_find_succeesor( - node1: &Swarm, - node2: &Swarm, - node3: &Swarm, - ) -> Result<()> { - // check node1 and node3 is not connected to each other - assert!(node1.get_connection(node3.did()).is_none()); - - // node1's successor should be node2 now - assert_eq!(node1.dht.successors().max()?, node2.did()); - - node1.connect(node3.did()).await.unwrap(); - - // node1 send msg to node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::ConnectNodeSend(_) - )); - - // node2 relay msg to node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node1.did(), node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::ConnectNodeSend(_) - )); - - // node3 send report to node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::ConnectNodeReport(_) - )); - - // node 2 relay report to node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node3.did(), node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::ConnectNodeReport(_) - )); - - // The following are communications after successful connection - - // 1 JoinDHT - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node1.did()); - assert_eq!(ev_1.relay.path, vec![node1.did()]); - assert!( - matches!(ev_1.transaction.data()?, Message::JoinDHT(JoinDHT{did, ..}) if did == node3.did()) - ); - - // 3 JoinDHT - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node3.did()); - assert_eq!(ev_3.relay.path, vec![node3.did()]); - assert!( - matches!(ev_3.transaction.data()?, Message::JoinDHT(JoinDHT{did, ..}) if did == node1.did()) - ); - - // 3->1 FindSuccessorSend - let ev_1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev_1.signer(), node3.did()); - assert_eq!(ev_1.relay.path, vec![node3.did()]); - assert!(matches!( - ev_1.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, strict: false, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect)}) if did == node3.did() - )); - - // 1->3 FindSuccessorSend - let ev_3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev_3.signer(), node1.did()); - assert_eq!(ev_3.relay.path, vec![node1.did()]); - assert!(matches!( - ev_3.transaction.data()?, - Message::FindSuccessorSend(FindSuccessorSend{did, strict: false, then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect)}) if did == node1.did() - )); - - Ok(()) - } - - fn assert_transports(swarm: Arc, addresses: Vec) { - println!( - "Check transport of {:?}: {:?} for addresses {:?}", - swarm.did(), - swarm.get_connection_ids(), - addresses - ); - assert_eq!(swarm.get_connections().len(), addresses.len()); - for addr in addresses { - assert!(swarm.get_connection(addr).is_some()); - } + Ok((node1, node2, node3)) } #[tokio::test] @@ -836,88 +444,76 @@ pub mod tests { // Therefore, here we only guarantee that messages can be processed correctly without checking the specific message order. // // In addition, we check the final state to ensure the entire process meets expectations. - tokio::select! { - _ = async { - futures::join!( - async { node1.clone().listen().await }, - async { node2.clone().listen().await }, - async { node3.clone().listen().await }, - async { node4.clone().listen().await }, - ) - } => {unreachable!();} - _ = async { - // connect node4 to node2 - manually_establish_connection(&node4, &node2).await; - tokio::time::sleep(Duration::from_secs(3)).await; - - println!("=== Check state before connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did(), node3.did(), node4.did()]); - assert_transports(node2.clone(), vec![node3.did(), node4.did(), node1.did()]); - assert_transports(node3.clone(), vec![node1.did(), node2.did()]); - // node4 will connect node1 after connecting node2, because node2 notified node4 that node1 is its predecessor. - assert_transports(node4.clone(), vec![node1.did(), node2.did()]); - assert_eq!(node1.dht().successors().list().unwrap(), vec![ - node2.did(), - node3.did(), - node4.did(), - ]); - assert_eq!(node2.dht().successors().list().unwrap(), vec![ - node3.did(), - node4.did(), - node1.did(), - ]); - assert_eq!(node3.dht().successors().list().unwrap(), vec![ - node1.did(), - node2.did(), - ]); - assert_eq!(node4.dht().successors().list().unwrap(), vec![ - node1.did(), - node2.did(), - ]); - - println!("========================================"); - println!("| test node4 connect node3 via dht |"); - println!("========================================"); - println!( - "node1.did(): {:?}, node2.did(): {:?}, node3.did(): {:?}, node4.did(): {:?}", - node1.did(), - node2.did(), - node3.did(), - node4.did(), - ); - println!("=================================================="); - - node4.connect(node3.did()).await.unwrap(); - tokio::time::sleep(Duration::from_secs(3)).await; - - println!("=== Check state after connect via DHT ==="); - assert_transports(node1.clone(), vec![node2.did(), node3.did(), node4.did()]); - assert_transports(node2.clone(), vec![node3.did(), node4.did(), node1.did()]); - assert_transports(node3.clone(), vec![node4.did(), node1.did(), node2.did()]); - assert_transports(node4.clone(), vec![node1.did(), node2.did(), node3.did()]); - assert_eq!(node1.dht().successors().list().unwrap(), vec![ - node2.did(), - node3.did(), - node4.did() - ]); - assert_eq!(node2.dht().successors().list().unwrap(), vec![ - node3.did(), - node4.did(), - node1.did(), - ]); - assert_eq!(node3.dht().successors().list().unwrap(), vec![ - node4.did(), - node1.did(), - node2.did(), - ]); - assert_eq!(node4.dht().successors().list().unwrap(), vec![ - node1.did(), - node2.did(), - node3.did(), - ]); - - } => {} - } + + // connect node4 to node2 + manually_establish_connection(&node4.swarm, &node2.swarm).await; + tokio::time::sleep(Duration::from_secs(6)).await; + + println!("=== Check state before connect via DHT ==="); + node1.assert_transports(vec![node2.did(), node3.did(), node4.did()]); + node2.assert_transports(vec![node3.did(), node4.did(), node1.did()]); + node3.assert_transports(vec![node1.did(), node2.did()]); + // node4 will connect node1 after connecting node2, because node2 notified node4 that node1 is its predecessor. + node4.assert_transports(vec![node1.did(), node2.did()]); + assert_eq!(node1.dht().successors().list().unwrap(), vec![ + node2.did(), + node3.did(), + node4.did(), + ]); + assert_eq!(node2.dht().successors().list().unwrap(), vec![ + node3.did(), + node4.did(), + node1.did(), + ]); + assert_eq!(node3.dht().successors().list().unwrap(), vec![ + node1.did(), + node2.did(), + ]); + assert_eq!(node4.dht().successors().list().unwrap(), vec![ + node1.did(), + node2.did(), + ]); + + println!("========================================"); + println!("| test node4 connect node3 via dht |"); + println!("========================================"); + println!( + "node1.did(): {:?}, node2.did(): {:?}, node3.did(): {:?}, node4.did(): {:?}", + node1.did(), + node2.did(), + node3.did(), + node4.did(), + ); + println!("=================================================="); + + node4.swarm.connect(node3.did()).await.unwrap(); + tokio::time::sleep(Duration::from_secs(6)).await; + + println!("=== Check state after connect via DHT ==="); + node1.assert_transports(vec![node2.did(), node3.did(), node4.did()]); + node2.assert_transports(vec![node3.did(), node4.did(), node1.did()]); + node3.assert_transports(vec![node4.did(), node1.did(), node2.did()]); + node4.assert_transports(vec![node1.did(), node2.did(), node3.did()]); + assert_eq!(node1.dht().successors().list().unwrap(), vec![ + node2.did(), + node3.did(), + node4.did() + ]); + assert_eq!(node2.dht().successors().list().unwrap(), vec![ + node3.did(), + node4.did(), + node1.did(), + ]); + assert_eq!(node3.dht().successors().list().unwrap(), vec![ + node4.did(), + node1.did(), + node2.did(), + ]); + assert_eq!(node4.dht().successors().list().unwrap(), vec![ + node1.did(), + node2.did(), + node3.did(), + ]); Ok(()) } @@ -939,11 +535,12 @@ pub mod tests { assert!(node1.dht().lock_finger()?.is_empty()); } - test_only_two_nodes_establish_connection(&node1, &node2).await?; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; - assert_transports(node1.clone(), vec![node2.did()]); - assert_transports(node2.clone(), vec![node1.did()]); + node1.assert_transports(vec![node2.did()]); + node2.assert_transports(vec![node1.did()]); { let finger1 = node1.dht().lock_finger()?.clone().clone_finger(); let finger2 = node2.dht().lock_finger()?.clone().clone_finger(); @@ -955,18 +552,16 @@ pub mod tests { println!("==================================="); println!("| test disconnect node1 and node2 |"); println!("==================================="); - node1.disconnect(node2.did()).await?; - - let ev1 = node1.listen_once().await.unwrap().0; - assert!( - matches!(ev1.transaction.data()?, Message::LeaveDHT(LeaveDHT{did}) if did == node2.did()) - ); + node1.swarm.disconnect(node2.did()).await?; for _ in 1..10 { println!("wait 3 seconds for node2's transport 2to1 closing"); sleep(Duration::from_secs(3)).await; - if let Some(t) = node2.get_connection(node1.did()) { - if t.is_disconnected().await { + if let Some(t) = node2.swarm.transport.get_connection(node1.did()) { + if matches!( + t.webrtc_connection_state(), + WebrtcConnectionState::Disconnected | WebrtcConnectionState::Closed + ) { println!("transport 2to1 is disconnected!!!!"); break; } @@ -976,24 +571,10 @@ pub mod tests { } } - // state change to Disconnected - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node2.did()); - assert!( - matches!(ev2.transaction.data()?, Message::LeaveDHT(LeaveDHT{did}) if did == node1.did()) - ); - - // state change to Closed - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node2.did()); - assert!( - matches!(ev2.transaction.data()?, Message::LeaveDHT(LeaveDHT{did}) if did == node1.did()) - ); - assert_no_more_msg(&node1, &node2, &node3).await; - assert_transports(node1.clone(), vec![]); - assert_transports(node2.clone(), vec![]); + node1.assert_transports(vec![]); + node2.assert_transports(vec![]); { let finger1 = node1.dht().lock_finger()?.clone().clone_finger(); let finger2 = node2.dht().lock_finger()?.clone().clone_finger(); diff --git a/crates/core/src/message/handlers/custom.rs b/crates/core/src/message/handlers/custom.rs index 08acfafad..1a10816b3 100644 --- a/crates/core/src/message/handlers/custom.rs +++ b/crates/core/src/message/handlers/custom.rs @@ -4,21 +4,16 @@ use crate::error::Result; use crate::message::types::CustomMessage; use crate::message::HandleMsg; use crate::message::MessageHandler; -use crate::message::MessageHandlerEvent; use crate::message::MessagePayload; +use crate::message::PayloadSender; #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - _: &CustomMessage, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, _: &CustomMessage) -> Result<()> { if self.dht.did != ctx.relay.destination { - Ok(vec![MessageHandlerEvent::ForwardPayload(ctx.clone(), None)]) - } else { - Ok(vec![]) + self.transport.forward_payload(ctx, None).await?; } + Ok(()) } } diff --git a/crates/core/src/message/handlers/dht.rs b/crates/core/src/message/handlers/dht.rs deleted file mode 100644 index 6acb23210..000000000 --- a/crates/core/src/message/handlers/dht.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![warn(missing_docs)] -//! This module provides helper function for handle DHT related Actions - -use async_recursion::async_recursion; - -use crate::dht::PeerRingAction; -use crate::dht::PeerRingRemoteAction; -use crate::error::Result; -use crate::message::types::FindSuccessorSend; -use crate::message::types::Message; -use crate::message::types::QueryForTopoInfoSend; -use crate::message::FindSuccessorReportHandler; -use crate::message::FindSuccessorThen; -use crate::message::MessageHandlerEvent; -use crate::message::MessagePayload; - -/// This accept a action instance, handler_func and error_msg string as parameter. -/// This macro is used for handling `PeerRingAction::MultiActions`. -/// -/// It accepts three parameters: -/// * `$actions`: This parameter represents the actions that will be processed. -/// * `$handler_func`: This is the handler function that will be used to process each action. It is expected to be -/// an expression that evaluates to a closure. The closure should be an asynchronous function -/// which accepts a single action and returns a `Result`. -/// The function will be called for each action in `$actions`, and should handle the action appropriately. -/// -/// * `$error_msg`: This is a string that will be used as the error message if the handler function returns an error. -/// The string should include one set of braces `{}` that will be filled with the `Debug` representation -/// of the error returned from the handler function. -/// -/// The macro returns a `Result>`. If all actions are processed successfully, it returns -/// `Ok(Vec)`, where the vector includes all the successful results from the handler function. -/// If any action fails, an error message will be logged, but the error will not be returned from the macro; instead, -/// it will continue with the next action. -/// -/// The macro is asynchronous, so it should be used within an `async` context. -#[macro_export] -macro_rules! handle_multi_actions { - ($actions:expr, $handler_func:expr, $error_msg:expr) => {{ - let ret: Vec = - futures::future::join_all($actions.iter().map($handler_func)) - .await - .iter() - .map(|x| { - if x.is_err() { - tracing::error!($error_msg, x) - }; - x - }) - .filter_map(|x| x.as_ref().ok()) - .flat_map(|xs| xs.iter()) - .cloned() - .collect(); - Ok(ret) - }}; -} - -/// Handler of join dht event from PeerRing DHT. -#[cfg_attr(feature = "wasm", async_recursion(?Send))] -#[cfg_attr(not(feature = "wasm"), async_recursion)] -pub async fn handle_dht_events( - act: &PeerRingAction, - ctx: &MessagePayload, -) -> Result> { - match act { - PeerRingAction::None => Ok(vec![]), - // Ask next hop to find successor for did, - // if there is only two nodes A, B, it may cause loop, for example - // A's successor is B, B ask A to find successor for B - // A may send message to it's successor, which is B - PeerRingAction::RemoteAction(next, PeerRingRemoteAction::FindSuccessorForConnect(did)) => { - if next != did { - Ok(vec![MessageHandlerEvent::SendDirectMessage( - Message::FindSuccessorSend(FindSuccessorSend { - did: *did, - strict: false, - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - }), - *next, - )]) - } else { - Ok(vec![]) - } - } - // A new successor is set, request the new successor for it's successor list - PeerRingAction::RemoteAction(next, PeerRingRemoteAction::QueryForSuccessorList) => { - Ok(vec![MessageHandlerEvent::SendDirectMessage( - Message::QueryForTopoInfoSend(QueryForTopoInfoSend::new_for_sync(*next)), - *next, - )]) - } - PeerRingAction::RemoteAction(did, PeerRingRemoteAction::TryConnect) => { - Ok(vec![MessageHandlerEvent::ConnectVia( - *did, - ctx.relay.origin_sender(), - )]) - } - PeerRingAction::RemoteAction(did, PeerRingRemoteAction::Notify(target_id)) => { - if did == target_id { - tracing::warn!("Did is equal to target_id, may implement wrong."); - return Ok(vec![]); - } - Ok(vec![MessageHandlerEvent::Notify(*target_id)]) - } - PeerRingAction::MultiActions(acts) => { - handle_multi_actions!( - acts, - |act| async { handle_dht_events(act, ctx).await }, - "Failed on handle multi actions: {:#?}" - ) - } - _ => unreachable!(), - } -} diff --git a/crates/core/src/message/handlers/mod.rs b/crates/core/src/message/handlers/mod.rs index 390d7689a..b27fc0202 100644 --- a/crates/core/src/message/handlers/mod.rs +++ b/crates/core/src/message/handlers/mod.rs @@ -1,33 +1,35 @@ #![warn(missing_docs)] //! This module implemented message handler of rings network. -/// Message Flow: -/// +---------+ +--------------------------------+ -/// | Message | -> | MessageHandler.handler_payload | -/// +---------+ +--------------------------------+ -/// || || -/// +--------------------------+ +--------------------------+ -/// | Builtin Message Callback | | Custom Message Callback | -/// +--------------------------+ +--------------------------+ + use std::sync::Arc; use async_recursion::async_recursion; use async_trait::async_trait; -use super::Message; use super::MessagePayload; -use crate::dht::vnode::VirtualNode; +use crate::dht::Chord; +use crate::dht::CorrectChord; use crate::dht::Did; use crate::dht::PeerRing; +use crate::dht::PeerRingAction; +use crate::dht::PeerRingRemoteAction; +use crate::error::Error; use crate::error::Result; -use crate::message::ConnectNodeReport; -use crate::message::ConnectNodeSend; +use crate::message::types::FindSuccessorSend; +use crate::message::types::Message; +use crate::message::types::QueryForTopoInfoSend; +use crate::message::FindSuccessorReportHandler; +use crate::message::FindSuccessorThen; +use crate::message::NotifyPredecessorSend; +use crate::message::PayloadSender; +use crate::swarm::callback::InnerSwarmCallback; +use crate::swarm::callback::SharedSwarmCallback; +use crate::swarm::transport::SwarmTransport; /// Operator and Handler for Connection pub mod connection; /// Operator and Handler for CustomMessage pub mod custom; -/// For handle dht related actions -pub mod dht; /// Operator and handler for DHT stablization pub mod stabilization; /// Operator and Handler for Storage @@ -35,56 +37,12 @@ pub mod storage; /// Operator and Handler for Subring pub mod subring; -type NextHop = Did; - -/// MessageHandlerEvent that will be handled by Swarm. -#[derive(Debug, Clone)] -pub enum MessageHandlerEvent { - /// Instructs the swarm to connect to a peer. - Connect(Did), - /// Instructs the swarm to connect to a peer via given next hop. - ConnectVia(Did, NextHop), - - /// Instructs the swarm to remove a peer in dht if it's not existed. - LeaveDHT(Did), - - /// Instructs the swarm to answer an offer inside payload by given - /// sender's Did and Message. - AnswerOffer(MessagePayload, ConnectNodeSend), - - /// Instructs the swarm to accept an answer inside payload by given - /// sender's Did and Message. - AcceptAnswer(Did, ConnectNodeReport), - - /// Tell swarm to forward the payload to destination by given - /// Payload and optional next hop. - ForwardPayload(MessagePayload, Option), - - /// Instructs the swarm to notify the dht about new peer. - JoinDHT(MessagePayload, Did), - - /// Instructs the swarm to send a direct message to a peer. - SendDirectMessage(Message, Did), - - /// Instructs the swarm to send a message to a peer via the dht network. - SendMessage(Message, Did), - - /// Instructs the swarm to send a message as a response to the received message. - SendReportMessage(MessagePayload, Message), - - /// Instructs the swarm to send a message to a peer via the dht network with a specific next hop. - ResetDestination(MessagePayload, Did), - - /// Instructs the swarm to store vnode. - StorageStore(VirtualNode), - /// Notify a node - Notify(Did), -} - /// MessageHandler will manage resources. #[derive(Clone)] pub struct MessageHandler { + transport: Arc, dht: Arc, + swarm_callback: SharedSwarmCallback, } /// Generic trait for handle message ,inspired by Actor-Model. @@ -92,242 +50,120 @@ pub struct MessageHandler { #[cfg_attr(not(feature = "wasm"), async_trait)] pub trait HandleMsg { /// Message handler. - async fn handle(&self, ctx: &MessagePayload, msg: &T) -> Result>; + async fn handle(&self, ctx: &MessagePayload, msg: &T) -> Result<()>; } impl MessageHandler { - /// Create a new MessageHandler Instance. - pub fn new(dht: Arc) -> Self { - Self { dht } - } - - /// Handle builtin message. - #[cfg_attr(feature = "wasm", async_recursion(?Send))] - #[cfg_attr(not(feature = "wasm"), async_recursion)] - pub async fn handle_message( - &self, - payload: &MessagePayload, - ) -> Result> { - let message: Message = payload.transaction.data()?; - - tracing::debug!( - "START HANDLE MESSAGE: {} {}", - &payload.transaction.tx_id, - &message - ); - - let events = match &message { - Message::JoinDHT(ref msg) => self.handle(payload, msg).await, - Message::LeaveDHT(ref msg) => self.handle(payload, msg).await, - Message::ConnectNodeSend(ref msg) => self.handle(payload, msg).await, - Message::ConnectNodeReport(ref msg) => self.handle(payload, msg).await, - Message::FindSuccessorSend(ref msg) => self.handle(payload, msg).await, - Message::FindSuccessorReport(ref msg) => self.handle(payload, msg).await, - Message::NotifyPredecessorSend(ref msg) => self.handle(payload, msg).await, - Message::NotifyPredecessorReport(ref msg) => self.handle(payload, msg).await, - Message::SearchVNode(ref msg) => self.handle(payload, msg).await, - Message::FoundVNode(ref msg) => self.handle(payload, msg).await, - Message::SyncVNodeWithSuccessor(ref msg) => self.handle(payload, msg).await, - Message::OperateVNode(ref msg) => self.handle(payload, msg).await, - Message::CustomMessage(ref msg) => self.handle(payload, msg).await, - Message::QueryForTopoInfoSend(ref msg) => self.handle(payload, msg).await, - Message::QueryForTopoInfoReport(ref msg) => self.handle(payload, msg).await, - Message::Chunk(_) => Ok(vec![]), - }?; - - tracing::debug!("FINISH HANDLE MESSAGE {}", &payload.transaction.tx_id); - Ok(events) + /// Create a new MessageHandler instance. + pub fn new(transport: Arc, swarm_callback: SharedSwarmCallback) -> Self { + let dht = transport.dht.clone(); + Self { + transport, + dht, + swarm_callback, + } } -} - -#[cfg(not(feature = "wasm"))] -#[cfg(test)] -pub mod tests { - use dashmap::DashMap; - use futures::lock::Mutex; - use tokio::time::sleep; - use tokio::time::Duration; - use super::*; - use crate::dht::Did; - use crate::ecc::SecretKey; - use crate::message::MessageVerificationExt; - use crate::message::PayloadSender; - use crate::swarm::callback::SwarmCallback; - use crate::swarm::Swarm; - use crate::tests::default::prepare_node; - use crate::tests::manually_establish_connection; - - struct SwarmCallbackInstance { - handler_messages: Mutex)>>, + fn inner_callback(&self) -> InnerSwarmCallback { + InnerSwarmCallback::new(self.transport.clone(), self.swarm_callback.clone()) } - #[tokio::test] - async fn test_custom_message_handling() -> Result<()> { - let key1 = SecretKey::random(); - let key2 = SecretKey::random(); - - #[async_trait] - impl SwarmCallback for SwarmCallbackInstance { - async fn on_inbound( - &self, - payload: &MessagePayload, - ) -> std::result::Result<(), Box> { - let msg: Message = payload.transaction.data().map_err(Box::new)?; - - match msg { - Message::CustomMessage(ref msg) => { - self.handler_messages - .lock() - .await - .push((payload.transaction.signer(), msg.0.clone())); - println!("{:?}, {:?}, {:?}", payload, payload.signer(), msg); - } - _ => { - println!("{:?}, {:?}", payload, payload.signer()); - } - } - - Ok(()) - } + pub(crate) async fn join_dht(&self, peer: Did) -> Result<()> { + if cfg!(feature = "experimental") { + let conn = self + .transport + .get_connection(peer) + .ok_or(Error::SwarmMissDidInTable(peer))?; + let dht_ev = self.dht.join_then_sync(conn).await?; + self.handle_dht_events(&dht_ev).await + } else { + let dht_ev = self.dht.join(peer)?; + self.handle_dht_events(&dht_ev).await.unwrap(); + Ok(()) } + } - let cb1 = Arc::new(SwarmCallbackInstance { - handler_messages: Mutex::new(vec![]), - }); - let cb2 = Arc::new(SwarmCallbackInstance { - handler_messages: Mutex::new(vec![]), - }); - - let node1 = prepare_node(key1).await; - let node2 = prepare_node(key2).await; - - node1.set_callback(cb1.clone()).unwrap(); - node2.set_callback(cb2.clone()).unwrap(); - - manually_establish_connection(&node1, &node2).await; - - let node11 = node1.clone(); - let node22 = node2.clone(); - tokio::spawn(async move { node11.listen().await }); - tokio::spawn(async move { node22.listen().await }); - - println!("waiting for data channel ready"); - sleep(Duration::from_secs(5)).await; - - println!("sending messages"); - node1 - .send_message( - Message::custom("Hello world 1 to 2 - 1".as_bytes())?, - node2.did(), - ) + pub(crate) async fn leave_dht(&self, peer: Did) -> Result<()> { + if self + .transport + .get_and_check_connection(peer) .await - .unwrap(); - - node1 - .send_message( - Message::custom("Hello world 1 to 2 - 2".as_bytes())?, - node2.did(), - ) - .await?; - - node2 - .send_message( - Message::custom("Hello world 2 to 1 - 1".as_bytes())?, - node1.did(), - ) - .await?; - - node1 - .send_message( - Message::custom("Hello world 1 to 2 - 3".as_bytes())?, - node2.did(), - ) - .await?; - - node2 - .send_message( - Message::custom("Hello world 2 to 1 - 2".as_bytes())?, - node1.did(), - ) - .await?; - - sleep(Duration::from_secs(5)).await; - - assert_eq!(cb1.handler_messages.lock().await.as_slice(), &[ - (node2.did(), "Hello world 2 to 1 - 1".as_bytes().to_vec()), - (node2.did(), "Hello world 2 to 1 - 2".as_bytes().to_vec()) - ]); - - assert_eq!(cb2.handler_messages.lock().await.as_slice(), &[ - (node1.did(), "Hello world 1 to 2 - 1".as_bytes().to_vec()), - (node1.did(), "Hello world 1 to 2 - 2".as_bytes().to_vec()), - (node1.did(), "Hello world 1 to 2 - 3".as_bytes().to_vec()) - ]); - + .is_none() + { + self.dht.remove(peer)? + }; Ok(()) } - pub async fn assert_no_more_msg(node1: &Swarm, node2: &Swarm, node3: &Swarm) { - tokio::select! { - _ = node1.listen_once() => unreachable!("node1 should not receive any message"), - _ = node2.listen_once() => unreachable!("node2 should not receive any message"), - _ = node3.listen_once() => unreachable!("node3 should not receive any message"), - _ = sleep(Duration::from_secs(3)) => {} - } - } - - pub async fn wait_for_msgs(node1: &Swarm, node2: &Swarm, node3: &Swarm) { - let did_names: DashMap = DashMap::new(); - did_names.insert(node1.did(), "node1"); - did_names.insert(node2.did(), "node2"); - did_names.insert(node3.did(), "node3"); - - let listen1 = async { - loop { - tokio::select! { - Some((payload, _)) = node1.listen_once() => { - println!( - "Msg {} => node1 : {:?}", - *did_names.get(&payload.signer()).unwrap(), - payload.transaction.data::().unwrap() + #[cfg_attr(feature = "wasm", async_recursion(?Send))] + #[cfg_attr(not(feature = "wasm"), async_recursion)] + pub(crate) async fn handle_dht_events(&self, act: &PeerRingAction) -> Result<()> { + match act { + PeerRingAction::None => Ok(()), + // Ask next hop to find successor for did, + // if there is only two nodes A, B, it may cause loop, for example + // A's successor is B, B ask A to find successor for B + // A may send message to it's successor, which is B + PeerRingAction::RemoteAction( + next, + PeerRingRemoteAction::FindSuccessorForConnect(did), + ) => { + if next != did { + self.transport + .send_direct_message( + Message::FindSuccessorSend(FindSuccessorSend { + did: *did, + strict: false, + then: FindSuccessorThen::Report( + FindSuccessorReportHandler::Connect, + ), + }), + *next, ) - } - _ = sleep(Duration::from_secs(3)) => break + .await?; + Ok(()) + } else { + Ok(()) } } - }; - - let listen2 = async { - loop { - tokio::select! { - Some((payload, _)) = node2.listen_once() => { - println!( - "Msg {} => node2 : {:?}", - *did_names.get(&payload.signer()).unwrap(), - payload.transaction.data::().unwrap() - ) - } - _ = sleep(Duration::from_secs(3)) => break + // A new successor is set, request the new successor for it's successor list + PeerRingAction::RemoteAction(next, PeerRingRemoteAction::QueryForSuccessorList) => { + self.transport + .send_direct_message( + Message::QueryForTopoInfoSend(QueryForTopoInfoSend::new_for_sync(*next)), + *next, + ) + .await?; + Ok(()) + } + PeerRingAction::RemoteAction(did, PeerRingRemoteAction::TryConnect) => { + self.transport.connect(*did, self.inner_callback()).await?; + Ok(()) + } + PeerRingAction::RemoteAction(did, PeerRingRemoteAction::Notify(target_id)) => { + if did == target_id { + tracing::warn!("Did is equal to target_id, may implement wrong."); + return Ok(()); } + let msg = + Message::NotifyPredecessorSend(NotifyPredecessorSend { did: self.dht.did }); + self.transport.send_message(msg, *target_id).await?; + Ok(()) } - }; - - let listen3 = async { - loop { - tokio::select! { - Some((payload, _)) = node3.listen_once() => { - println!( - "Msg {} => node3 : {:?}", - *did_names.get(&payload.signer()).unwrap(), - payload.transaction.data::().unwrap() - ) + PeerRingAction::MultiActions(acts) => { + let jobs = acts + .iter() + .map(|act| async move { self.handle_dht_events(act).await }); + + for res in futures::future::join_all(jobs).await { + if res.is_err() { + tracing::error!("Failed on handle multi actions: {:#?}", res) } - _ = sleep(Duration::from_secs(3)) => break } - } - }; - futures::join!(listen1, listen2, listen3); + Ok(()) + } + _ => unreachable!(), + } } } diff --git a/crates/core/src/message/handlers/stabilization.rs b/crates/core/src/message/handlers/stabilization.rs index 450e54f28..3471cf083 100644 --- a/crates/core/src/message/handlers/stabilization.rs +++ b/crates/core/src/message/handlers/stabilization.rs @@ -11,52 +11,51 @@ use crate::message::types::NotifyPredecessorSend; use crate::message::types::SyncVNodeWithSuccessor; use crate::message::HandleMsg; use crate::message::MessageHandler; -use crate::message::MessageHandlerEvent; use crate::message::MessagePayload; +use crate::message::PayloadSender; #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &NotifyPredecessorSend, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &NotifyPredecessorSend) -> Result<()> { let predecessor = self.dht.notify(msg.did)?; if predecessor != ctx.relay.origin_sender() { - return Ok(vec![MessageHandlerEvent::SendReportMessage( - ctx.clone(), - Message::NotifyPredecessorReport(NotifyPredecessorReport { did: predecessor }), - )]); + return self + .transport + .send_report_message( + ctx, + Message::NotifyPredecessorReport(NotifyPredecessorReport { did: predecessor }), + ) + .await; } - Ok(vec![]) + Ok(()) } } #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - _ctx: &MessagePayload, - msg: &NotifyPredecessorReport, - ) -> Result> { - let mut events = vec![MessageHandlerEvent::Connect(msg.did)]; + async fn handle(&self, _ctx: &MessagePayload, msg: &NotifyPredecessorReport) -> Result<()> { + self.transport + .connect(msg.did, self.inner_callback()) + .await?; if let Ok(PeerRingAction::RemoteAction( next, PeerRingRemoteAction::SyncVNodeWithSuccessor(data), )) = self.dht.sync_vnode_with_successor(msg.did).await { - events.push(MessageHandlerEvent::SendMessage( - Message::SyncVNodeWithSuccessor(SyncVNodeWithSuccessor { data }), - next, - )) + self.transport + .send_message( + Message::SyncVNodeWithSuccessor(SyncVNodeWithSuccessor { data }), + next, + ) + .await?; } - Ok(events) + Ok(()) } } @@ -67,23 +66,12 @@ mod test { use super::*; use crate::dht::successor::SuccessorReader; - use crate::dht::Stabilization; use crate::ecc::tests::gen_ordered_keys; use crate::ecc::SecretKey; - use crate::message::handlers::connection::tests::test_listen_join_and_init_find_succeesor; - use crate::message::handlers::connection::tests::test_only_two_nodes_establish_connection; - use crate::message::handlers::tests::assert_no_more_msg; - use crate::message::handlers::tests::wait_for_msgs; - use crate::message::ConnectNodeReport; - use crate::message::ConnectNodeSend; - use crate::message::FindSuccessorReport; - use crate::message::FindSuccessorReportHandler; - use crate::message::FindSuccessorSend; - use crate::message::FindSuccessorThen; - use crate::message::JoinDHT; - use crate::message::MessageVerificationExt; use crate::swarm::Swarm; + use crate::tests::default::assert_no_more_msg; use crate::tests::default::prepare_node; + use crate::tests::default::wait_for_msgs; use crate::tests::manually_establish_connection; #[tokio::test] @@ -142,16 +130,16 @@ mod test { println!("|| now we connect node1 and node2 ||"); println!("========================================"); - test_only_two_nodes_establish_connection(&node1, &node2).await?; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; println!("========================================"); println!("|| now we start join node3 to node2 ||"); println!("========================================"); - manually_establish_connection(&node3, &node2).await; - test_listen_join_and_init_find_succeesor(&node3, &node2).await?; - node3.listen_once().await.unwrap(); - node2.listen_once().await.unwrap(); + manually_establish_connection(&node3.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state before stabilization ==="); @@ -169,69 +157,11 @@ mod test { println!("|| now we start first stabilization ||"); println!("========================================"); - run_stabilize_once(node1.clone()).await?; - run_stabilize_once(node2.clone()).await?; - run_stabilize_once(node3.clone()).await?; - - // node2 notify node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node1 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node1.did() - )); - - // node2 notify node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node3 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node2 report node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorReport(NotifyPredecessorReport{did}) if did == node1.did() - )); - - // handle messages of connection after stabilization - node2.listen_once().await.unwrap(); - node1.listen_once().await.unwrap(); - node2.listen_once().await.unwrap(); - node3.listen_once().await.unwrap(); - node1.listen_once().await.unwrap(); - node3.listen_once().await.unwrap(); - node3.listen_once().await.unwrap(); - node1.listen_once().await.unwrap(); - node1.listen_once().await.unwrap(); - node2.listen_once().await.unwrap(); - node1.listen_once().await.unwrap(); - node3.listen_once().await.unwrap(); + run_stabilization_once(node1.swarm.clone()).await?; + run_stabilization_once(node2.swarm.clone()).await?; + run_stabilization_once(node3.swarm.clone()).await?; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after first stabilization ==="); @@ -261,82 +191,11 @@ mod test { println!("|| now we start second stabilization ||"); println!("========================================="); - run_stabilize_once(node1.clone()).await?; - run_stabilize_once(node2.clone()).await?; - run_stabilize_once(node3.clone()).await?; - - // node1 notify node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node1.did()); - assert_eq!(ev3.relay.path, vec![node1.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node1.did() - )); - - // node2 notify node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node3 notify node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node3.did()); - assert_eq!(ev1.relay.path, vec![node3.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node1 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node1.did() - )); - - // node3 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node2 notify node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node2 report node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorReport(NotifyPredecessorReport{did}) if did == node1.did() - )); - - // node3 report node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node3.did()); - assert_eq!(ev1.relay.path, vec![node3.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorReport(NotifyPredecessorReport{did}) if did == node2.did() - )); + run_stabilization_once(node1.swarm.clone()).await?; + run_stabilization_once(node2.swarm.clone()).await?; + run_stabilization_once(node3.swarm.clone()).await?; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after second stabilization ==="); @@ -377,18 +236,16 @@ mod test { println!("|| now we connect node1 and node2 ||"); println!("========================================"); - test_only_two_nodes_establish_connection(&node1, &node2).await?; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; println!("========================================"); println!("|| now we start join node3 to node2 ||"); println!("========================================"); - manually_establish_connection(&node3, &node2).await; - test_listen_join_and_init_find_succeesor(&node3, &node2).await?; - node1.listen_once().await.unwrap(); - node2.listen_once().await.unwrap(); - node2.listen_once().await.unwrap(); - node3.listen_once().await.unwrap(); + manually_establish_connection(&node3.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state before stabilization ==="); @@ -406,46 +263,11 @@ mod test { println!("|| now we start first stabilization ||"); println!("========================================"); - run_stabilize_once(node1.clone()).await?; - run_stabilize_once(node2.clone()).await?; - run_stabilize_once(node3.clone()).await?; - - // node2 notify node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node1 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node1.did() - )); - - // node3 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node2 notify node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); + run_stabilization_once(node1.swarm.clone()).await?; + run_stabilization_once(node2.swarm.clone()).await?; + run_stabilization_once(node3.swarm.clone()).await?; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after first stabilization ==="); @@ -469,202 +291,11 @@ mod test { println!("|| now we start second stabilization ||"); println!("========================================="); - run_stabilize_once(node1.clone()).await?; - run_stabilize_once(node2.clone()).await?; - run_stabilize_once(node3.clone()).await?; - - // node2 notify node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node2 notify node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node2.did() - )); - - // node1 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node1.did() - )); - - // node3 notify node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::NotifyPredecessorSend(NotifyPredecessorSend{did}) if did == node3.did() - )); - - // node2 notify node1 the existence of node3 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::NotifyPredecessorReport(NotifyPredecessorReport{did}) if did == node3.did() - )); - - // node1 connect node3 via node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node1.did()); - assert_eq!(ev2.relay.path, vec![node1.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::ConnectNodeSend(ConnectNodeSend { .. }) - )); - assert_eq!(ev2.transaction.destination, node3.did()); - - // node2 forward the message to node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node1.did(), node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::ConnectNodeSend(ConnectNodeSend { .. }) - )); - assert_eq!(ev3.transaction.destination, node3.did()); - - // node3 respond node1 via node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::ConnectNodeReport(ConnectNodeReport { .. }) - )); - assert_eq!(ev2.transaction.destination, node1.did()); - - // node2 forward the message to node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node2.did()); - assert_eq!(ev1.relay.path, vec![node3.did(), node2.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::ConnectNodeReport(ConnectNodeReport { .. }) - )); - assert_eq!(ev1.transaction.destination, node1.did()); - - // node1 JoinDHT - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node1.did()); - assert_eq!(ev1.relay.path, vec![node1.did()]); - assert!( - matches!(ev1.transaction.data()?, Message::JoinDHT(JoinDHT{did}) if did == node3.did()) - ); - - // node3 JoinDHT - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node3.did()); - assert_eq!(ev3.relay.path, vec![node3.did()]); - assert!( - matches!(ev3.transaction.data()?, Message::JoinDHT(JoinDHT{did}) if did == node1.did()) - ); - - // node3 FindSuccessorSend to node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node3.did()); - assert_eq!(ev1.relay.path, vec![node3.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::FindSuccessorSend( - FindSuccessorSend{ - did, - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - strict: false - } - ) - if did == node3.did() - )); - - // node1 FindSuccessorSend to node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node1.did()); - assert_eq!(ev3.relay.path, vec![node1.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::FindSuccessorSend( - FindSuccessorSend{ - did, - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - strict: false - } - ) - if did == node1.did() - )); - - // node1 FindSuccessorReport to node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node1.did()); - assert_eq!(ev3.relay.path, vec![node1.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::FindSuccessorReport(FindSuccessorReport { - did, - handler: FindSuccessorReportHandler::Connect, - }) - if did == node3.did() - )); - - // node3 forward FindSuccessorSend to node2 - let ev2 = node2.listen_once().await.unwrap().0; - assert_eq!(ev2.signer(), node3.did()); - assert_eq!(ev2.relay.path, vec![node1.did(), node3.did()]); - assert!(matches!( - ev2.transaction.data()?, - Message::FindSuccessorSend( - FindSuccessorSend{ - did, - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - strict: false - } - ) - if did == node1.did() - )); - - // node2 FindSuccessorReport to node3 - let ev3 = node3.listen_once().await.unwrap().0; - assert_eq!(ev3.signer(), node2.did()); - assert_eq!(ev3.relay.path, vec![node2.did()]); - assert!(matches!( - ev3.transaction.data()?, - Message::FindSuccessorReport( - FindSuccessorReport { - did, - handler: FindSuccessorReportHandler::Connect, - } - ) - if did == node1.did() - )); - - // node3 forward FindSuccessorReport to node1 - let ev1 = node1.listen_once().await.unwrap().0; - assert_eq!(ev1.signer(), node3.did()); - assert_eq!(ev1.relay.path, vec![node2.did(), node3.did()]); - assert!(matches!( - ev1.transaction.data()?, - Message::FindSuccessorReport( - FindSuccessorReport { - did, - handler: FindSuccessorReportHandler::Connect, - } - ) - if did == node1.did() - )); + run_stabilization_once(node1.swarm.clone()).await?; + run_stabilization_once(node2.swarm.clone()).await?; + run_stabilization_once(node3.swarm.clone()).await?; + wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; println!("=== Check state after second stabilization ==="); @@ -694,9 +325,9 @@ mod test { println!("|| now we start third stabilization ||"); println!("========================================="); - run_stabilize_once(node1.clone()).await?; - run_stabilize_once(node2.clone()).await?; - run_stabilize_once(node3.clone()).await?; + run_stabilization_once(node1.swarm.clone()).await?; + run_stabilization_once(node2.swarm.clone()).await?; + run_stabilization_once(node3.swarm.clone()).await?; wait_for_msgs(&node1, &node2, &node3).await; assert_no_more_msg(&node1, &node2, &node3).await; @@ -727,8 +358,8 @@ mod test { Ok(()) } - async fn run_stabilize_once(swarm: Arc) -> Result<()> { - let stab = Stabilization::new(swarm, 5); + async fn run_stabilization_once(swarm: Arc) -> Result<()> { + let stab = swarm.stabilizer(); stab.notify_predecessor().await } } diff --git a/crates/core/src/message/handlers/storage.rs b/crates/core/src/message/handlers/storage.rs index 022c0eaf7..052f3021b 100644 --- a/crates/core/src/message/handlers/storage.rs +++ b/crates/core/src/message/handlers/storage.rs @@ -1,4 +1,7 @@ #![warn(missing_docs)] + +use std::sync::Arc; + use async_recursion::async_recursion; use async_trait::async_trait; @@ -11,7 +14,6 @@ use crate::dht::PeerRingAction; use crate::dht::PeerRingRemoteAction; use crate::error::Error; use crate::error::Result; -use crate::handle_multi_actions; use crate::message::types::FoundVNode; use crate::message::types::Message; use crate::message::types::SearchVNode; @@ -19,10 +21,10 @@ use crate::message::types::SyncVNodeWithSuccessor; use crate::message::Encoded; use crate::message::HandleMsg; use crate::message::MessageHandler; -use crate::message::MessageHandlerEvent; use crate::message::MessagePayload; use crate::message::PayloadSender; use crate::prelude::vnode::VNodeOperation; +use crate::swarm::transport::SwarmTransport; use crate::swarm::Swarm; /// ChordStorageInterface should imply necessary method for DHT storage @@ -52,11 +54,14 @@ pub trait ChordStorageInterfaceCacheChecker { /// Handle the storage fetch action of the peer ring. #[cfg_attr(feature = "wasm", async_recursion(?Send))] #[cfg_attr(not(feature = "wasm"), async_recursion)] -async fn handle_storage_fetch_act(swarm: &Swarm, act: PeerRingAction) -> Result<()> { +async fn handle_storage_fetch_act( + transport: Arc, + act: PeerRingAction, +) -> Result<()> { match act { PeerRingAction::None => (), PeerRingAction::SomeVNode(v) => { - swarm.dht.local_cache_put(v).await?; + transport.dht.local_cache_put(v).await?; } PeerRingAction::RemoteAction(next, dht_act) => { if let PeerRingRemoteAction::FindVNode(vid) = dht_act { @@ -65,14 +70,14 @@ async fn handle_storage_fetch_act(swarm: &Swarm, act: PeerRingAction) -> Result< vid, next ); - swarm + transport .send_message(Message::SearchVNode(SearchVNode { vid }), next) .await?; } } PeerRingAction::MultiActions(acts) => { for act in acts { - handle_storage_fetch_act(swarm, act).await?; + handle_storage_fetch_act(transport.clone(), act).await?; } } act => return Err(Error::PeerRingUnexpectedAction(act)), @@ -83,17 +88,20 @@ async fn handle_storage_fetch_act(swarm: &Swarm, act: PeerRingAction) -> Result< /// Handle the storage store operations of the peer ring. #[cfg_attr(feature = "wasm", async_recursion(?Send))] #[cfg_attr(not(feature = "wasm"), async_recursion)] -pub(super) async fn handle_storage_store_act(swarm: &Swarm, act: PeerRingAction) -> Result<()> { +pub(super) async fn handle_storage_store_act( + transport: Arc, + act: PeerRingAction, +) -> Result<()> { match act { PeerRingAction::None => (), PeerRingAction::RemoteAction(target, PeerRingRemoteAction::FindVNodeForOperate(op)) => { - swarm + transport .send_message(Message::OperateVNode(op), target) .await?; } PeerRingAction::MultiActions(acts) => { for act in acts { - handle_storage_store_act(swarm, act).await?; + handle_storage_store_act(transport.clone(), act).await?; } } act => return Err(Error::PeerRingUnexpectedAction(act)), @@ -104,28 +112,34 @@ pub(super) async fn handle_storage_store_act(swarm: &Swarm, act: PeerRingAction) /// Handle the storage store operations of the peer ring. #[cfg_attr(feature = "wasm", async_recursion(?Send))] #[cfg_attr(not(feature = "wasm"), async_recursion)] -pub(super) async fn handle_storage_search_act( +async fn handle_storage_search_act( + transport: Arc, ctx: &MessagePayload, act: PeerRingAction, -) -> Result> { +) -> Result<()> { match act { - PeerRingAction::None => Ok(vec![]), - PeerRingAction::SomeVNode(v) => Ok(vec![MessageHandlerEvent::SendReportMessage( - ctx.clone(), - Message::FoundVNode(FoundVNode { data: vec![v] }), - )]), - PeerRingAction::RemoteAction(next, _) => Ok(vec![MessageHandlerEvent::ResetDestination( - ctx.clone(), - next, - )]), + PeerRingAction::None => Ok(()), + PeerRingAction::SomeVNode(v) => { + transport + .send_report_message(ctx, Message::FoundVNode(FoundVNode { data: vec![v] })) + .await + } + PeerRingAction::RemoteAction(next, _) => transport.reset_destination(ctx, next).await, PeerRingAction::MultiActions(acts) => { - handle_multi_actions!( - acts, - |act| async move { handle_storage_search_act(ctx, act.clone()).await }, - "Failed on handle multi actions: {:#?}" - ) + let jobs = acts.iter().map(|act| { + let transport_clone = transport.clone(); + async move { handle_storage_operate_act(transport_clone, ctx, act).await } + }); + + for res in futures::future::join_all(jobs).await { + if res.is_err() { + tracing::error!("Failed on handle multi actions: {:#?}", res) + } + } + + Ok(()) } - act => Err(Error::PeerRingUnexpectedAction(act)), + act => Err(Error::PeerRingUnexpectedAction(act.clone())), } } @@ -133,21 +147,26 @@ pub(super) async fn handle_storage_search_act( #[cfg_attr(feature = "wasm", async_recursion(?Send))] #[cfg_attr(not(feature = "wasm"), async_recursion)] pub(super) async fn handle_storage_operate_act( + transport: Arc, ctx: &MessagePayload, act: &PeerRingAction, -) -> Result> { +) -> Result<()> { match act { - PeerRingAction::None => Ok(vec![]), - PeerRingAction::RemoteAction(next, _) => Ok(vec![MessageHandlerEvent::ResetDestination( - ctx.clone(), - *next, - )]), + PeerRingAction::None => Ok(()), + PeerRingAction::RemoteAction(next, _) => transport.reset_destination(ctx, *next).await, PeerRingAction::MultiActions(acts) => { - handle_multi_actions!( - acts, - |act| async move { handle_storage_operate_act(ctx, act).await }, - "Failed on handle multi actions: {:#?}" - ) + let jobs = acts.iter().map(|act| { + let transport_clone = transport.clone(); + async move { handle_storage_operate_act(transport_clone, ctx, act).await } + }); + + for res in futures::future::join_all(jobs).await { + if res.is_err() { + tracing::error!("Failed on handle multi actions: {:#?}", res) + } + } + + Ok(()) } act => Err(Error::PeerRingUnexpectedAction(act.clone())), } @@ -170,7 +189,7 @@ impl ChordStorageInterface for Swarm { async fn storage_fetch(&self, vid: Did) -> Result<()> { // If peer found that data is on it's localstore, copy it to the cache let act = >::vnode_lookup(&self.dht, vid).await?; - handle_storage_fetch_act(self, act).await?; + handle_storage_fetch_act(self.transport.clone(), act).await?; Ok(()) } @@ -178,7 +197,7 @@ impl ChordStorageInterface for Swarm { async fn storage_store(&self, vnode: VirtualNode) -> Result<()> { let op = VNodeOperation::Overwrite(vnode); let act = >::vnode_operate(&self.dht, op).await?; - handle_storage_store_act(self, act).await?; + handle_storage_store_act(self.transport.clone(), act).await?; Ok(()) } @@ -186,7 +205,7 @@ impl ChordStorageInterface for Swarm { let vnode: VirtualNode = (topic.to_string(), data).try_into()?; let op = VNodeOperation::Extend(vnode); let act = >::vnode_operate(&self.dht, op).await?; - handle_storage_store_act(self, act).await?; + handle_storage_store_act(self.transport.clone(), act).await?; Ok(()) } @@ -194,7 +213,7 @@ impl ChordStorageInterface for Swarm { let vnode: VirtualNode = (topic.to_string(), data).try_into()?; let op = VNodeOperation::Touch(vnode); let act = >::vnode_operate(&self.dht, op).await?; - handle_storage_store_act(self, act).await?; + handle_storage_store_act(self.transport.clone(), act).await?; Ok(()) } } @@ -204,14 +223,10 @@ impl ChordStorageInterface for Swarm { impl HandleMsg for MessageHandler { /// Search VNode via successor /// If a VNode is storead local, it will response immediately.(See Chordstorageinterface::storage_fetch) - async fn handle( - &self, - ctx: &MessagePayload, - msg: &SearchVNode, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &SearchVNode) -> Result<()> { // For relay message, set redundant to 1 match >::vnode_lookup(&self.dht, msg.vid).await { - Ok(action) => handle_storage_search_act(ctx, action).await, + Ok(action) => handle_storage_search_act(self.transport.clone(), ctx, action).await, Err(e) => Err(e), } } @@ -220,33 +235,25 @@ impl HandleMsg for MessageHandler { #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &FoundVNode, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &FoundVNode) -> Result<()> { if self.dht.did != ctx.relay.destination { - return Ok(vec![MessageHandlerEvent::ForwardPayload(ctx.clone(), None)]); + return self.transport.forward_payload(ctx, None).await; } for data in msg.data.iter().cloned() { self.dht.local_cache_put(data).await?; } - Ok(vec![]) + Ok(()) } } #[cfg_attr(feature = "wasm", async_trait(?Send))] #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { - async fn handle( - &self, - ctx: &MessagePayload, - msg: &VNodeOperation, - ) -> Result> { + async fn handle(&self, ctx: &MessagePayload, msg: &VNodeOperation) -> Result<()> { // For relay message, set redundant to 1 let action = >::vnode_operate(&self.dht, msg.clone()).await?; - handle_storage_operate_act(ctx, &action).await + handle_storage_operate_act(self.transport.clone(), ctx, &action).await } } @@ -254,18 +261,15 @@ impl HandleMsg for MessageHandler { #[cfg_attr(not(feature = "wasm"), async_trait)] impl HandleMsg for MessageHandler { // received remote sync vnode request - async fn handle( - &self, - _ctx: &MessagePayload, - msg: &SyncVNodeWithSuccessor, - ) -> Result> { - let mut events = vec![]; + async fn handle(&self, _ctx: &MessagePayload, msg: &SyncVNodeWithSuccessor) -> Result<()> { for data in msg.data.iter().cloned() { // only simply store here // For relay message, set redundant to 1 - events.push(MessageHandlerEvent::StorageStore(data)); + let op = VNodeOperation::Overwrite(data); + let act = >::vnode_operate(&self.dht, op).await?; + handle_storage_store_act(self.transport.clone(), act).await?; } - Ok(events) + Ok(()) } } @@ -274,18 +278,26 @@ impl HandleMsg for MessageHandler { mod test { use super::*; use crate::ecc::tests::gen_ordered_keys; - use crate::message::handlers::connection::tests::test_only_two_nodes_establish_connection; use crate::message::Encoder; use crate::prelude::vnode::VNodeType; + use crate::tests::default::assert_no_more_msg; use crate::tests::default::prepare_node; + use crate::tests::default::wait_for_msgs; + use crate::tests::manually_establish_connection; #[tokio::test] async fn test_store_vnode() -> Result<()> { - let keys = gen_ordered_keys(2); - let (key1, key2) = (keys[0], keys[1]); + let keys = gen_ordered_keys(3); + let (key1, key2, key3) = (keys[0], keys[1], keys[2]); let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - test_only_two_nodes_establish_connection(&node1, &node2).await?; + + // This is only a dummy node for using assert_no_more_msg function + let node3 = prepare_node(key3).await; + + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; // Now, node1 is the successor of node2, and node2 is the successor of node1. // Following tests storing data on node2 and query it from node1. @@ -302,45 +314,45 @@ mod test { assert_eq!(node1.dht().cache.count().await.unwrap(), 0); assert_eq!(node2.dht().cache.count().await.unwrap(), 0); - assert!(node1.storage_check_cache(vid).await.is_none()); - assert!(node2.storage_check_cache(vid).await.is_none()); + assert!(node1.swarm.storage_check_cache(vid).await.is_none()); + assert!(node2.swarm.storage_check_cache(vid).await.is_none()); - >::storage_store(&node1, vnode.clone()) + >::storage_store(&node1.swarm, vnode.clone()) .await .unwrap(); - let ev = node2.listen_once().await.unwrap().0; + let ev = node2.listen_once().await.unwrap(); assert!(matches!( ev.transaction.data()?, Message::OperateVNode(VNodeOperation::Overwrite(x)) if x.did == vid )); - assert!(node1.storage_check_cache(vid).await.is_none()); - assert!(node2.storage_check_cache(vid).await.is_none()); + assert!(node1.swarm.storage_check_cache(vid).await.is_none()); + assert!(node2.swarm.storage_check_cache(vid).await.is_none()); assert!(node1.dht().storage.count().await.unwrap() == 0); assert!(node2.dht().storage.count().await.unwrap() != 0); // test remote query println!("vid is on node2 {:?}", node2.did()); - >::storage_fetch(&node1, vid) + >::storage_fetch(&node1.swarm, vid) .await .unwrap(); // it will send request to node2 - let ev = node2.listen_once().await.unwrap().0; + let ev = node2.listen_once().await.unwrap(); // node2 received search vnode request assert!(matches!( ev.transaction.data()?, Message::SearchVNode(x) if x.vid == vid )); - let ev = node1.listen_once().await.unwrap().0; + let ev = node1.listen_once().await.unwrap(); assert!(matches!( ev.transaction.data()?, Message::FoundVNode(x) if x.data[0].did == vid )); assert_eq!( - node1.storage_check_cache(vid).await, + node1.swarm.storage_check_cache(vid).await, Some(VirtualNode { did: vid, data: vec![data.encode()?], @@ -354,11 +366,17 @@ mod test { #[cfg(not(feature = "redundant"))] #[tokio::test] async fn test_extend_data() -> Result<()> { - let keys = gen_ordered_keys(2); - let (key1, key2) = (keys[0], keys[1]); + let keys = gen_ordered_keys(3); + let (key1, key2, key3) = (keys[0], keys[1], keys[2]); let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - test_only_two_nodes_establish_connection(&node1, &node2).await?; + + // This is only a dummy node for using assert_no_more_msg function + let node3 = prepare_node(key3).await; + + manually_establish_connection(&node1.swarm, &node2.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; // Now, node1 is the successor of node2, and node2 is the successor of node1. // Following tests storing data on node2 and query it from node1. @@ -375,63 +393,44 @@ mod test { assert_eq!(node1.dht().cache.count().await.unwrap(), 0); assert_eq!(node2.dht().cache.count().await.unwrap(), 0); - assert!(node1.storage_check_cache(vid).await.is_none()); - assert!(node2.storage_check_cache(vid).await.is_none()); + assert!(node1.swarm.storage_check_cache(vid).await.is_none()); + assert!(node2.swarm.storage_check_cache(vid).await.is_none()); >::storage_append_data( - &node1, + &node1.swarm, &topic, "111".to_string().encode()?, ) .await .unwrap(); + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; - let ev = node2.listen_once().await.unwrap().0; - assert!(matches!( - ev.transaction.data()?, - Message::OperateVNode(VNodeOperation::Extend(VirtualNode { did, data, kind: VNodeType::Data })) - if did == vid && data == vec!["111".to_string().encode()?] - )); >::storage_append_data( - &node1, + &node1.swarm, &topic, "222".to_string().encode()?, ) .await .unwrap(); - let ev = node2.listen_once().await.unwrap().0; - assert!(matches!( - ev.transaction.data()?, - Message::OperateVNode(VNodeOperation::Extend(VirtualNode { did, data, kind: VNodeType::Data })) - if did == vid && data == vec!["222".to_string().encode()?] - )); - assert!(node1.storage_check_cache(vid).await.is_none()); - assert!(node2.storage_check_cache(vid).await.is_none()); + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; + + assert!(node1.swarm.storage_check_cache(vid).await.is_none()); + assert!(node2.swarm.storage_check_cache(vid).await.is_none()); assert!(node1.dht().storage.count().await.unwrap() == 0); assert!(node2.dht().storage.count().await.unwrap() != 0); + // test remote query println!("vid is on node2 {:?}", node2.did()); - >::storage_fetch(&node1, vid) + >::storage_fetch(&node1.swarm, vid) .await .unwrap(); - - // it will send request to node2 - let ev = node2.listen_once().await.unwrap().0; - - // node2 received search vnode request - assert!(matches!( - ev.transaction.data()?, - Message::SearchVNode(x) if x.vid == vid - )); - let ev = node1.listen_once().await.unwrap().0; - - assert!(matches!( - ev.transaction.data()?, - Message::FoundVNode(x) if x.data[0].did == vid - )); + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; assert_eq!( - node1.storage_check_cache(vid).await, + node1.swarm.storage_check_cache(vid).await, Some(VirtualNode { did: vid, data: vec!["111".to_string().encode()?, "222".to_string().encode()?], @@ -441,42 +440,25 @@ mod test { // Append more data >::storage_append_data( - &node1, + &node1.swarm, &topic, "333".to_string().encode()?, ) .await .unwrap(); - - let ev = node2.listen_once().await.unwrap().0; - assert!(matches!( - ev.transaction.data()?, - Message::OperateVNode(VNodeOperation::Extend(VirtualNode { did, data, kind: VNodeType::Data })) - if did == vid && data == vec!["333".to_string().encode()?] - )); + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; // test remote query agagin println!("vid is on node2 {:?}", node2.did()); - >::storage_fetch(&node1, vid) + >::storage_fetch(&node1.swarm, vid) .await .unwrap(); - - // it will send request to node2 - let ev = node2.listen_once().await.unwrap().0; - // node2 received search vnode request - assert!(matches!( - ev.transaction.data()?, - Message::SearchVNode(x) if x.vid == vid - )); - - let ev = node1.listen_once().await.unwrap().0; - assert!(matches!( - ev.transaction.data()?, - Message::FoundVNode(x) if x.data[0].did == vid - )); + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; assert_eq!( - node1.storage_check_cache(vid).await, + node1.swarm.storage_check_cache(vid).await, Some(VirtualNode { did: vid, data: vec![ diff --git a/crates/core/src/message/handlers/subring.rs b/crates/core/src/message/handlers/subring.rs index 739f7f623..4f4566228 100644 --- a/crates/core/src/message/handlers/subring.rs +++ b/crates/core/src/message/handlers/subring.rs @@ -24,7 +24,7 @@ impl SubringInterface for Swarm { async fn subring_join(&self, name: &str) -> Result<()> { let op = VNodeOperation::JoinSubring(name.to_string(), self.dht.did); let act = >::vnode_operate(&self.dht, op).await?; - handle_storage_store_act(self, act).await?; + handle_storage_store_act(self.transport.clone(), act).await?; Ok(()) } } diff --git a/crates/core/src/message/mod.rs b/crates/core/src/message/mod.rs index 4440d15e6..7613fae6d 100644 --- a/crates/core/src/message/mod.rs +++ b/crates/core/src/message/mod.rs @@ -22,7 +22,6 @@ pub use handlers::storage::ChordStorageInterfaceCacheChecker; pub use handlers::subring::SubringInterface; pub use handlers::HandleMsg; pub use handlers::MessageHandler; -pub use handlers::MessageHandlerEvent; mod protocols; pub use protocols::MessageRelay; diff --git a/crates/core/src/message/payload.rs b/crates/core/src/message/payload.rs index 2e64853c8..dfa36dc84 100644 --- a/crates/core/src/message/payload.rs +++ b/crates/core/src/message/payload.rs @@ -290,6 +290,7 @@ pub trait PayloadSender { let next_hop = self.infer_next_hop(destination, None)?; self.send_message_by_hop(msg, destination, next_hop).await } + /// Send a direct message to a specified destination. async fn send_direct_message(&self, msg: T, destination: Did) -> Result where T: Serialize + Send { diff --git a/crates/core/src/message/types.rs b/crates/core/src/message/types.rs index 9b93e8d86..fd18c8158 100644 --- a/crates/core/src/message/types.rs +++ b/crates/core/src/message/types.rs @@ -131,20 +131,6 @@ impl Then for QueryForTopoInfoSend { type Then = QueryFor; } -/// MessageType use to join chord ring, add did into fingers table. -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct JoinDHT { - /// The did for joining - pub did: Did, -} - -/// MessageType use to leave chord ring. -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct LeaveDHT { - /// The did for dropping - pub did: Did, -} - /// MessageType use to search virtual node. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct SearchVNode { @@ -196,10 +182,6 @@ pub enum FindSuccessorReportHandler { #[derive(Debug, Deserialize, Serialize, Clone)] #[non_exhaustive] pub enum Message { - /// Local message of Join a node to DHT - JoinDHT(JoinDHT), - /// Local message of drop a node from DHT - LeaveDHT(LeaveDHT), /// Remote message of try connecting a node. ConnectNodeSend(ConnectNodeSend), /// Response of ConnectNodeSend diff --git a/crates/core/src/swarm/builder.rs b/crates/core/src/swarm/builder.rs index 7ee8eb922..f43dd6123 100644 --- a/crates/core/src/swarm/builder.rs +++ b/crates/core/src/swarm/builder.rs @@ -5,17 +5,14 @@ use std::sync::Arc; use std::sync::RwLock; -use crate::channels::Channel; use crate::dht::PeerRing; use crate::dht::VNodeStorage; -use crate::message::MessageHandler; +use crate::measure::MeasureImpl; use crate::session::SessionSk; use crate::swarm::callback::SharedSwarmCallback; use crate::swarm::callback::SwarmCallback; -use crate::swarm::MeasureImpl; +use crate::swarm::transport::SwarmTransport; use crate::swarm::Swarm; -use crate::types::channel::Channel as ChannelTrait; -use crate::types::Transport; struct DefaultCallback; impl SwarmCallback for DefaultCallback {} @@ -88,22 +85,21 @@ impl SwarmBuilder { self.dht_storage, )); - let message_handler = MessageHandler::new(dht.clone()); - - let transport_event_channel = Channel::new(); - let transport = Box::new(Transport::new(&self.ice_servers, self.external_address)); - let callback = RwLock::new( self.callback .unwrap_or_else(|| Arc::new(DefaultCallback {})), ); + let transport = Arc::new(SwarmTransport::new( + &self.ice_servers, + self.external_address, + self.session_sk, + dht.clone(), + self.measure, + )); + Swarm { - transport_event_channel, dht, - measure: self.measure, - session_sk: self.session_sk, - message_handler, transport, callback, } diff --git a/crates/core/src/swarm/callback.rs b/crates/core/src/swarm/callback.rs index dcd91c459..3ebaeb02e 100644 --- a/crates/core/src/swarm/callback.rs +++ b/crates/core/src/swarm/callback.rs @@ -6,18 +6,17 @@ use futures::lock::Mutex as FuturesMutex; use rings_transport::core::callback::TransportCallback; use rings_transport::core::transport::WebrtcConnectionState; -use crate::channels::Channel; use crate::chunk::ChunkList; use crate::chunk::ChunkManager; use crate::consts::TRANSPORT_MTU; use crate::dht::Did; +use crate::message::HandleMsg; use crate::message::Message; +use crate::message::MessageHandler; use crate::message::MessagePayload; use crate::message::MessageVerificationExt; -use crate::types::channel::Channel as ChannelTrait; -use crate::types::channel::TransportEvent; +use crate::swarm::transport::SwarmTransport; -type TransportEventSender = as ChannelTrait>::Sender; type CallbackError = Box; /// The [InnerSwarmCallback] will accept shared [SwarmCallback] trait object. @@ -64,22 +63,19 @@ pub trait SwarmCallback { /// [InnerSwarmCallback] wraps [SharedSwarmCallback] with inner handling for a specific connection. pub struct InnerSwarmCallback { - did: Did, - transport_event_sender: TransportEventSender, + transport: Arc, + message_handler: MessageHandler, callback: SharedSwarmCallback, - chunk_list: Arc>>, + chunk_list: FuturesMutex>, } impl InnerSwarmCallback { - /// Create a new [InnerSwarmCallback] with the provided did, transport_event_sender and callback. - pub fn new( - did: Did, - transport_event_sender: TransportEventSender, - callback: SharedSwarmCallback, - ) -> Self { + /// Create a new [InnerSwarmCallback] with the provided transport and callback. + pub fn new(transport: Arc, callback: SharedSwarmCallback) -> Self { + let message_handler = MessageHandler::new(transport.clone(), callback.clone()); Self { - did, - transport_event_sender, + transport, + message_handler, callback, chunk_list: Default::default(), } @@ -92,14 +88,44 @@ impl InnerSwarmCallback { ) -> Result<(), CallbackError> { let message: Message = payload.transaction.data()?; - if let Message::Chunk(msg) = message { - if let Some(data) = self.chunk_list.lock().await.handle(msg.clone()) { - return self.on_message(cid, &data).await; + match &message { + Message::ConnectNodeSend(ref msg) => self.message_handler.handle(payload, msg).await, + Message::ConnectNodeReport(ref msg) => self.message_handler.handle(payload, msg).await, + Message::FindSuccessorSend(ref msg) => self.message_handler.handle(payload, msg).await, + Message::FindSuccessorReport(ref msg) => { + self.message_handler.handle(payload, msg).await } - return Ok(()); - }; + Message::NotifyPredecessorSend(ref msg) => { + self.message_handler.handle(payload, msg).await + } + Message::NotifyPredecessorReport(ref msg) => { + self.message_handler.handle(payload, msg).await + } + Message::SearchVNode(ref msg) => self.message_handler.handle(payload, msg).await, + Message::FoundVNode(ref msg) => self.message_handler.handle(payload, msg).await, + Message::SyncVNodeWithSuccessor(ref msg) => { + self.message_handler.handle(payload, msg).await + } + Message::OperateVNode(ref msg) => self.message_handler.handle(payload, msg).await, + Message::CustomMessage(ref msg) => self.message_handler.handle(payload, msg).await, + Message::QueryForTopoInfoSend(ref msg) => { + self.message_handler.handle(payload, msg).await + } + Message::QueryForTopoInfoReport(ref msg) => { + self.message_handler.handle(payload, msg).await + } + Message::Chunk(ref msg) => { + if let Some(data) = self.chunk_list.lock().await.handle(msg.clone()) { + return self.on_message(cid, &data).await; + } + Ok(()) + } + } + .unwrap_or_else(|e| { + tracing::error!("Failed to handle_payload: {:?}", e); + }); - if payload.transaction.destination == self.did { + if payload.transaction.destination == self.transport.dht.did { self.callback.on_inbound(payload).await?; } @@ -117,14 +143,6 @@ impl TransportCallback for InnerSwarmCallback { return Err("Cannot verify msg or it's expired".into()); } self.callback.on_validate(&payload).await?; - - Channel::send( - &self.transport_event_sender, - TransportEvent::DataChannelMessage(msg.into()), - ) - .await - .map_err(Box::new)?; - self.handle_payload(cid, &payload).await } @@ -139,22 +157,44 @@ impl TransportCallback for InnerSwarmCallback { }; match s { - WebrtcConnectionState::Connected => { - Channel::send(&self.transport_event_sender, TransportEvent::Connected(did)).await - } WebrtcConnectionState::Failed | WebrtcConnectionState::Disconnected | WebrtcConnectionState::Closed => { - Channel::send(&self.transport_event_sender, TransportEvent::Closed(did)).await + self.message_handler.leave_dht(did).await?; } - _ => Ok(()), + _ => {} + }; + + // Should use the `on_data_channel_open` function to notify the Connected state. + // It prevents users from blocking the channel creation while + // waiting for data channel opening in send_message. + if s != WebrtcConnectionState::Connected { + self.callback + .on_event(&SwarmEvent::ConnectionStateChange { + peer: did, + state: s, + }) + .await? } - .map_err(Box::new)?; + Ok(()) + } + + async fn on_data_channel_open(&self, cid: &str) -> Result<(), CallbackError> { + let Ok(did) = Did::from_str(cid) else { + tracing::warn!("on_data_channel_open parse did failed: {}", cid); + return Ok(()); + }; + + self.message_handler.join_dht(did).await?; + + // Notify Connected state here instead of on_peer_connection_state_change. + // It prevents users from blocking the channel creation while + // waiting for data channel opening in send_message. self.callback .on_event(&SwarmEvent::ConnectionStateChange { - peer: did, - state: s, + peer: self.transport.dht.did, + state: WebrtcConnectionState::Connected, }) .await } diff --git a/crates/core/src/swarm/impls.rs b/crates/core/src/swarm/impls.rs deleted file mode 100644 index 160900bfb..000000000 --- a/crates/core/src/swarm/impls.rs +++ /dev/null @@ -1,451 +0,0 @@ -use std::str::FromStr; - -use async_trait::async_trait; -use rings_transport::core::transport::ConnectionInterface; -use rings_transport::core::transport::WebrtcConnectionState; - -use super::callback::InnerSwarmCallback; -use crate::dht::Did; -use crate::error::Error; -use crate::error::Result; -use crate::measure::MeasureCounter; -use crate::message::ConnectNodeReport; -use crate::message::ConnectNodeSend; -use crate::message::Message; -use crate::message::MessagePayload; -use crate::message::MessageVerificationExt; -use crate::message::PayloadSender; -use crate::swarm::callback::SharedSwarmCallback; -use crate::swarm::Swarm; -use crate::types::channel::Channel; -use crate::types::Connection; - -/// ConnectionHandshake defined how to connect two connections between two swarms. -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait ConnectionHandshake { - /// Create new connection and its offer. - async fn prepare_connection_offer(&self, peer: Did) -> Result<(Connection, ConnectNodeSend)>; - - /// Answer the offer of remote connection. - async fn answer_remote_connection( - &self, - peer: Did, - offer_msg: &ConnectNodeSend, - ) -> Result<(Connection, ConnectNodeReport)>; - - /// Accept the answer of remote connection. - async fn accept_remote_connection( - &self, - peer: Did, - answer_msg: &ConnectNodeReport, - ) -> Result; - - /// Creaet new connection and its answer. This function will wrap the offer inside a payload - /// with verification. - async fn create_offer(&self, peer: Did) -> Result<(Connection, MessagePayload)>; - - /// Answer the offer of remote connection. This function will verify the answer payload and - /// will wrap the answer inside a payload with verification. - async fn answer_offer( - &self, - offer_payload: MessagePayload, - ) -> Result<(Connection, MessagePayload)>; - - /// Accept the answer of remote connection. This function will verify the answer payload and - /// will return its did with the connection. - async fn accept_answer(&self, answer_payload: MessagePayload) -> Result<(Did, Connection)>; -} - -/// A trait for managing connections. -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait ConnectionManager { - /// Asynchronously disconnects the connection associated with the provided DID. - async fn disconnect(&self, did: Did) -> Result<()>; - - /// Asynchronously establishes a new connection and returns the connection associated with the provided DID. - async fn connect(&self, did: Did) -> Result; - - /// Asynchronously establishes a new connection via a specified next hop DID and returns the connection associated with the provided DID. - async fn connect_via(&self, did: Did, next_hop: Did) -> Result; -} - -/// A trait for judging whether a connection should be established with a given DID (Decentralized Identifier). -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait Judegement { - /// Asynchronously checks if a connection should be established with the provided DID. - async fn should_connect(&self, did: Did) -> bool; - - /// Asynchronously records that a connection has been established with the provided DID. - async fn record_connect(&self, did: Did); - - /// Asynchronously records that a connection has been disconnected with the provided DID. - async fn record_disconnected(&self, did: Did); -} - -/// A trait that combines the `Judegement` and `ConnectionManager` traits. -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait JudgeConnection: Judegement + ConnectionManager { - /// Asynchronously disconnects the connection associated with the provided DID after recording the disconnection. - async fn disconnect(&self, did: Did) -> Result<()> { - self.record_disconnected(did).await; - tracing::debug!("[JudegeConnection] Disconnected {:?}", &did); - ConnectionManager::disconnect(self, did).await - } - - /// Asynchronously establishes a new connection and returns the connection associated with the provided DID if `should_connect` returns true; otherwise, returns an error. - async fn connect(&self, did: Did) -> Result { - if !self.should_connect(did).await { - return Err(Error::NodeBehaviourBad(did)); - } - tracing::debug!("[JudgeConnection] Try Connect {:?}", &did); - self.record_connect(did).await; - ConnectionManager::connect(self, did).await - } - - /// Asynchronously establishes a new connection via a specified next hop DID and returns the connection associated with the provided DID if `should_connect` returns true; otherwise, returns an error. - async fn connect_via(&self, did: Did, next_hop: Did) -> Result { - if !self.should_connect(did).await { - return Err(Error::NodeBehaviourBad(did)); - } - tracing::debug!("[JudgeConnection] Try Connect {:?}", &did); - self.record_connect(did).await; - ConnectionManager::connect_via(self, did, next_hop).await - } -} - -impl Swarm { - /// Record a succeeded message sent - pub async fn record_sent(&self, did: Did) { - if let Some(measure) = &self.measure { - measure.incr(did, MeasureCounter::Sent).await; - } - } - - /// Record a failed message sent - pub async fn record_sent_failed(&self, did: Did) { - if let Some(measure) = &self.measure { - measure.incr(did, MeasureCounter::FailedToSend).await; - } - } - - /// Check that a Did is behaviour good - pub async fn behaviour_good(&self, did: Did) -> bool { - if let Some(measure) = &self.measure { - measure.good(did).await - } else { - true - } - } - - fn callback(&self) -> Result { - let inner = self - .callback - .read() - .map_err(|_| Error::CallbackSyncLockError)?; - - Ok(inner.clone()) - } - - /// Set callback for swarm. - pub fn set_callback(&self, callback: SharedSwarmCallback) -> Result<()> { - let mut inner = self - .callback - .write() - .map_err(|_| Error::CallbackSyncLockError)?; - - *inner = callback; - - Ok(()) - } - - /// Create new connection that will be handled by swarm. - pub async fn new_connection(&self, did: Did) -> Result { - let inner_callback = InnerSwarmCallback::new( - self.did(), - self.transport_event_channel.sender(), - self.callback()?, - ); - - let cid = did.to_string(); - self.transport - .new_connection(&cid, Box::new(inner_callback)) - .await - .map_err(Error::Transport)?; - self.transport.connection(&cid).map_err(|e| e.into()) - } - - /// Get connection by did and check if data channel is open. - /// This method will return None if the connection is not found. - /// This method will wait_for_data_channel_open. - /// If it's not ready in 8 seconds this method will close it and return None. - /// If it's ready in 8 seconds this method will return the connection. - /// See more information about [rings_transport::core::transport::WebrtcConnectionState]. - /// See also method webrtc_wait_for_data_channel_open [rings_transport::core::transport::ConnectionInterface]. - pub async fn get_and_check_connection(&self, did: Did) -> Option { - let Some(conn) = self.get_connection(did) else { - return None; - }; - - if let Err(e) = conn.webrtc_wait_for_data_channel_open().await { - tracing::warn!( - "[get_and_check_connection] connection {did} data channel not open, will be dropped, reason: {e:?}" - ); - - if let Err(e) = self.disconnect(did).await { - tracing::error!("Failed on close connection {did}: {e:?}"); - } - - return None; - }; - - Some(conn) - } - - /// Get connection by did. - pub fn get_connection(&self, did: Did) -> Option { - self.transport.connection(&did.to_string()).ok() - } - - /// Get all connections in transport. - pub fn get_connections(&self) -> Vec<(Did, Connection)> { - self.transport - .connections() - .into_iter() - .filter_map(|(k, v)| Did::from_str(&k).ok().map(|did| (did, v))) - .collect() - } - - /// Get dids of all connections in transport. - pub fn get_connection_ids(&self) -> Vec { - self.transport - .connection_ids() - .into_iter() - .filter_map(|k| Did::from_str(&k).ok()) - .collect() - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl ConnectionHandshake for Swarm { - async fn prepare_connection_offer(&self, peer: Did) -> Result<(Connection, ConnectNodeSend)> { - if self.get_and_check_connection(peer).await.is_some() { - return Err(Error::AlreadyConnected); - }; - - let conn = self.new_connection(peer).await?; - - let offer = conn.webrtc_create_offer().await.map_err(Error::Transport)?; - let offer_str = serde_json::to_string(&offer).map_err(|_| Error::SerializeToString)?; - let offer_msg = ConnectNodeSend { sdp: offer_str }; - - Ok((conn, offer_msg)) - } - - async fn answer_remote_connection( - &self, - peer: Did, - offer_msg: &ConnectNodeSend, - ) -> Result<(Connection, ConnectNodeReport)> { - if let Some(conn) = self.get_connection(peer) { - // Solve the scenario of creating offers simultaneously. - // - // When both sides create_offer at the same time and trigger answer_offer of the other side, - // they will got existed New state connection when answer_offer, which will prevent - // it to create new connection to answer the offer. - // - // The party with a larger Did (ranked lower on the ring) should abandon their own offer and instead answer_offer to the other party. - // The party with a smaller Did should reject answering the other party and report an Error::AlreadyConnected error. - if conn.webrtc_connection_state() == WebrtcConnectionState::New { - // drop local offer and continue answer remote offer - if self.did() > peer { - // this connection will replaced by new connection created bellow - self.disconnect(peer).await?; - } else { - // ignore remote offer, and refuse to answer remote offer - return Err(Error::AlreadyConnected); - } - } else if self.get_and_check_connection(peer).await.is_some() { - return Err(Error::AlreadyConnected); - }; - }; - - let offer = serde_json::from_str(&offer_msg.sdp).map_err(Error::Deserialize)?; - let conn = self.new_connection(peer).await?; - let answer = conn - .webrtc_answer_offer(offer) - .await - .map_err(Error::Transport)?; - let answer_str = serde_json::to_string(&answer).map_err(|_| Error::SerializeToString)?; - let answer_msg = ConnectNodeReport { sdp: answer_str }; - - Ok((conn, answer_msg)) - } - - async fn accept_remote_connection( - &self, - peer: Did, - answer_msg: &ConnectNodeReport, - ) -> Result { - let answer = serde_json::from_str(&answer_msg.sdp).map_err(Error::Deserialize)?; - - let conn = self.get_connection(peer).ok_or(Error::ConnectionNotFound)?; - conn.webrtc_accept_answer(answer) - .await - .map_err(Error::Transport)?; - - Ok(conn) - } - - async fn create_offer(&self, peer: Did) -> Result<(Connection, MessagePayload)> { - let (conn, offer_msg) = self.prepare_connection_offer(peer).await?; - - // This payload has fake next_hop. - // The invoker should fix it before sending. - let payload = MessagePayload::new_send( - Message::ConnectNodeSend(offer_msg), - self.session_sk(), - self.did(), - peer, - )?; - - Ok((conn, payload)) - } - - async fn answer_offer( - &self, - offer_payload: MessagePayload, - ) -> Result<(Connection, MessagePayload)> { - if !offer_payload.verify() { - return Err(Error::VerifySignatureFailed); - } - - let Message::ConnectNodeSend(msg) = offer_payload.transaction.data()? else { - return Err(Error::InvalidMessage( - "Should be ConnectNodeSend".to_string(), - )); - }; - - let peer = offer_payload.relay.origin_sender(); - let (conn, answer_msg) = self.answer_remote_connection(peer, &msg).await?; - - // This payload has fake next_hop. - // The invoker should fix it before sending. - let answer_payload = MessagePayload::new_send( - Message::ConnectNodeReport(answer_msg), - self.session_sk(), - self.did(), - self.did(), - )?; - - Ok((conn, answer_payload)) - } - - async fn accept_answer(&self, answer_payload: MessagePayload) -> Result<(Did, Connection)> { - tracing::debug!("accept_answer: {:?}", answer_payload); - - if !answer_payload.verify() { - return Err(Error::VerifySignatureFailed); - } - - let Message::ConnectNodeReport(ref msg) = answer_payload.transaction.data()? else { - return Err(Error::InvalidMessage( - "Should be ConnectNodeReport".to_string(), - )); - }; - - let peer = answer_payload.relay.origin_sender(); - let conn = self.accept_remote_connection(peer, msg).await?; - - Ok((peer, conn)) - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl ConnectionManager for Swarm { - /// Disconnect a connection. There are three steps: - /// 1) remove from DHT; - /// 2) remove from Transport; - /// 3) close the connection; - async fn disconnect(&self, did: Did) -> Result<()> { - tracing::info!("[disconnect] removing from DHT {:?}", did); - self.dht.remove(did)?; - self.transport - .close_connection(&did.to_string()) - .await - .map_err(|e| e.into()) - } - - /// Connect a given Did. If the did is already connected, return directly, - /// else try prepare offer and establish connection by dht. - /// This function may returns a pending connection or connected connection. - async fn connect(&self, did: Did) -> Result { - tracing::info!("Try connect Did {:?}", &did); - if let Some(t) = self.get_and_check_connection(did).await { - return Ok(t); - } - - let conn = self.new_connection(did).await?; - - let offer = conn.webrtc_create_offer().await.map_err(Error::Transport)?; - let offer_str = serde_json::to_string(&offer).map_err(|_| Error::SerializeToString)?; - let offer_msg = ConnectNodeSend { sdp: offer_str }; - - self.send_message(Message::ConnectNodeSend(offer_msg), did) - .await?; - - Ok(conn) - } - - /// Similar to connect, but this function will try connect a Did by given hop. - async fn connect_via(&self, did: Did, next_hop: Did) -> Result { - if let Some(t) = self.get_and_check_connection(did).await { - return Ok(t); - } - - tracing::info!("Try connect Did {:?}", &did); - - let conn = self.new_connection(did).await?; - - let offer = conn.webrtc_create_offer().await.map_err(Error::Transport)?; - let offer_str = serde_json::to_string(&offer).map_err(|_| Error::SerializeToString)?; - let offer_msg = ConnectNodeSend { sdp: offer_str }; - - self.send_message_by_hop(Message::ConnectNodeSend(offer_msg), did, next_hop) - .await?; - - Ok(conn) - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl Judegement for Swarm { - /// Record a succeeded connected - async fn record_connect(&self, did: Did) { - tracing::info!("Record connect {:?}", &did); - if let Some(measure) = &self.measure { - tracing::info!("[Judgement] Record connect"); - measure.incr(did, MeasureCounter::Connect).await; - } - } - - /// Record a disconnected - async fn record_disconnected(&self, did: Did) { - tracing::info!("Record disconnected {:?}", &did); - if let Some(measure) = &self.measure { - tracing::info!("[Judgement] Record disconnected"); - measure.incr(did, MeasureCounter::Disconnected).await; - } - } - - /// Asynchronously checks if a connection should be established with the provided DID. - async fn should_connect(&self, did: Did) -> bool { - self.behaviour_good(did).await - } -} diff --git a/crates/core/src/swarm/mod.rs b/crates/core/src/swarm/mod.rs index 63b8d407b..9e169aab2 100644 --- a/crates/core/src/swarm/mod.rs +++ b/crates/core/src/swarm/mod.rs @@ -1,68 +1,38 @@ #![warn(missing_docs)] -//! Tranposrt management + +//! This mod is the main entrance of swarm. mod builder; /// Callback interface for swarm pub mod callback; -/// Implementations of connection management traits for swarm -pub mod impls; -mod types; +pub(crate) mod transport; use std::sync::Arc; use std::sync::RwLock; -use async_recursion::async_recursion; -use async_trait::async_trait; pub use builder::SwarmBuilder; -use rings_derive::JudgeConnection; -use rings_transport::core::transport::BoxedTransport; -use rings_transport::core::transport::ConnectionInterface; -use rings_transport::core::transport::TransportMessage; -use rings_transport::core::transport::WebrtcConnectionState; -use rings_transport::error::Error as TransportError; -pub use types::MeasureImpl; -pub use types::WrappedDid; -use crate::channels::Channel; -use crate::chunk::ChunkList; -use crate::consts::TRANSPORT_MAX_SIZE; -use crate::consts::TRANSPORT_MTU; -use crate::dht::types::Chord; -use crate::dht::CorrectChord; +use self::callback::InnerSwarmCallback; use crate::dht::Did; use crate::dht::PeerRing; +use crate::dht::Stabilizer; use crate::error::Error; use crate::error::Result; +use crate::inspect::ConnectionInspect; use crate::inspect::SwarmInspect; -use crate::message; -use crate::message::types::NotifyPredecessorSend; -use crate::message::ChordStorageInterface; use crate::message::Message; -use crate::message::MessageHandler; -use crate::message::MessageHandlerEvent; use crate::message::MessagePayload; use crate::message::MessageVerificationExt; use crate::message::PayloadSender; -use crate::session::SessionSk; use crate::swarm::callback::SharedSwarmCallback; -use crate::swarm::impls::ConnectionHandshake; -use crate::types::channel::Channel as ChannelTrait; -use crate::types::channel::TransportEvent; -use crate::types::Connection; -use crate::types::ConnectionOwner; +use crate::swarm::transport::SwarmTransport; /// The transport and dht management. -#[derive(JudgeConnection)] pub struct Swarm { - /// Event channel for receive events from transport. - pub(crate) transport_event_channel: Channel, /// Reference of DHT. pub(crate) dht: Arc, - /// Implementationof measurement. - pub(crate) measure: Option, - session_sk: SessionSk, - message_handler: MessageHandler, - transport: BoxedTransport, + /// Swarm tansport. + pub(crate) transport: Arc, callback: RwLock, } @@ -77,233 +47,71 @@ impl Swarm { self.dht.clone() } - /// Retrieves the session sk associated with the current instance. - /// The session sk provides a segregated approach to manage private keys. - /// It generates session secret keys for the bound entries of PKIs (Public Key Infrastructure). - pub fn session_sk(&self) -> &SessionSk { - &self.session_sk + fn callback(&self) -> Result { + Ok(self + .callback + .read() + .map_err(|_| Error::CallbackSyncLockError)? + .clone()) } - /// Load message from a TransportEvent. - async fn load_message(&self, ev: TransportEvent) -> Result> { - match ev { - TransportEvent::DataChannelMessage(msg) => { - let payload = MessagePayload::from_bincode(&msg)?; - tracing::debug!("load message from channel: {:?}", payload); - Ok(Some(payload)) - } - TransportEvent::Connected(did) => match self.get_connection(did) { - Some(_) => { - let payload = MessagePayload::new_send( - Message::JoinDHT(message::JoinDHT { did }), - &self.session_sk, - self.dht.did, - self.dht.did, - )?; - Ok(Some(payload)) - } - None => Err(Error::SwarmMissTransport(did)), - }, - TransportEvent::Closed(did) => { - let payload = MessagePayload::new_send( - Message::LeaveDHT(message::LeaveDHT { did }), - &self.session_sk, - self.dht.did, - self.dht.did, - )?; - Ok(Some(payload)) - } - } + fn inner_callback(&self) -> Result { + Ok(InnerSwarmCallback::new( + self.transport.clone(), + self.callback()?, + )) } - /// This method is required because web-sys components is not `Send` - /// which means an async loop cannot running concurrency. - pub async fn poll_message(&self) -> Option { - let receiver = &self.transport_event_channel.receiver(); - match Channel::recv(receiver).await { - Ok(Some(ev)) => match self.load_message(ev).await { - Ok(Some(msg)) => Some(msg), - Ok(None) => None, - Err(_) => None, - }, - Ok(None) => None, - Err(e) => { - tracing::error!("Failed on polling message, Error {}", e); - None - } - } - } + /// Set callback for swarm. + pub fn set_callback(&self, callback: SharedSwarmCallback) -> Result<()> { + let mut inner = self + .callback + .write() + .map_err(|_| Error::CallbackSyncLockError)?; - /// This method is required because web-sys components is not `Send` - /// This method will return events already consumed (landed), which is ok to be ignore. - /// which means a listening loop cannot running concurrency. - pub async fn listen_once(&self) -> Option<(MessagePayload, Vec)> { - let payload = self.poll_message().await?; + *inner = callback; - if !(payload.verify() && payload.transaction.verify()) { - tracing::error!("Cannot verify msg or it's expired: {:?}", payload); - return None; - } - let events = self.message_handler.handle_message(&payload).await; - - match events { - Ok(evs) => { - self.handle_message_handler_events(&evs) - .await - .unwrap_or_else(|e| { - tracing::error!( - "Swarm failed on handling event from message handler: {:#?}", - e - ); - }); - Some((payload, evs)) - } - Err(e) => { - tracing::error!("Message handler failed on handling event: {:#?}", e); - None - } - } + Ok(()) } - /// Event handler of Swarm. - pub async fn handle_message_handler_event( - &self, - event: &MessageHandlerEvent, - ) -> Result> { - tracing::debug!("Handle message handler event: {:?}", event); - match event { - MessageHandlerEvent::Connect(did) => { - let did = *did; - if did != self.did() { - self.connect(did).await?; - } - Ok(vec![]) - } - - // Notify did with self.id - MessageHandlerEvent::Notify(did) => { - let msg = - Message::NotifyPredecessorSend(NotifyPredecessorSend { did: self.dht.did }); - Ok(vec![MessageHandlerEvent::SendMessage(msg, *did)]) - } - - MessageHandlerEvent::ConnectVia(did, next) => { - let did = *did; - if did != self.did() { - self.connect_via(did, *next).await?; - } - Ok(vec![]) - } - - MessageHandlerEvent::LeaveDHT(did) => { - let did = *did; - if self.get_and_check_connection(did).await.is_none() { - self.dht.remove(did)? - }; - Ok(vec![]) - } - - MessageHandlerEvent::AnswerOffer(relay, msg) => { - let (_, answer) = self - .answer_remote_connection(relay.relay.origin_sender(), msg) - .await?; - - Ok(vec![MessageHandlerEvent::SendReportMessage( - relay.clone(), - Message::ConnectNodeReport(answer), - )]) - } - - MessageHandlerEvent::AcceptAnswer(origin_sender, msg) => { - self.accept_remote_connection(origin_sender.to_owned(), msg) - .await?; - Ok(vec![]) - } - - MessageHandlerEvent::ForwardPayload(payload, next_hop) => { - self.forward_payload(payload, *next_hop).await?; - Ok(vec![]) - } - - MessageHandlerEvent::JoinDHT(ctx, did) => { - if cfg!(feature = "experimental") { - let wdid: WrappedDid = WrappedDid::new(self, *did); - let dht_ev = self.dht.join_then_sync(wdid).await?; - crate::message::handlers::dht::handle_dht_events(&dht_ev, ctx).await - } else { - let dht_ev = self.dht.join(*did)?; - crate::message::handlers::dht::handle_dht_events(&dht_ev, ctx).await - } - } - - MessageHandlerEvent::SendDirectMessage(msg, dest) => { - self.send_direct_message(msg.clone(), *dest).await?; - Ok(vec![]) - } - - MessageHandlerEvent::SendMessage(msg, dest) => { - self.send_message(msg.clone(), *dest).await?; - Ok(vec![]) - } - - MessageHandlerEvent::SendReportMessage(payload, msg) => { - self.send_report_message(payload, msg.clone()).await?; - Ok(vec![]) - } - - MessageHandlerEvent::ResetDestination(payload, next_hop) => { - self.reset_destination(payload, *next_hop).await?; - Ok(vec![]) - } - - MessageHandlerEvent::StorageStore(vnode) => { - >::storage_store(self, vnode.clone()).await?; - Ok(vec![]) - } - } - } - - /// Batch handle events - #[cfg_attr(feature = "wasm", async_recursion(?Send))] - #[cfg_attr(not(feature = "wasm"), async_recursion)] - pub async fn handle_message_handler_events( - &self, - events: &Vec, - ) -> Result<()> { - match events.as_slice() { - [] => Ok(()), - [x, xs @ ..] => { - let evs = self.handle_message_handler_event(x).await?; - self.handle_message_handler_events(&evs).await?; - self.handle_message_handler_events(&xs.to_vec()).await - } - } + /// Create [Stabilizer] for swarm. + pub fn stabilizer(&self) -> Stabilizer { + Stabilizer::new(self.transport.clone()) } /// Disconnect a connection. There are three steps: /// 1) remove from DHT; /// 2) remove from Transport; /// 3) close the connection; - pub async fn disconnect(&self, did: Did) -> Result<()> { - JudgeConnection::disconnect(self, did).await + pub async fn disconnect(&self, peer: Did) -> Result<()> { + self.transport.disconnect(peer).await } /// Connect a given Did. If the did is already connected, return directly, /// else try prepare offer and establish connection by dht. /// This function may returns a pending connection or connected connection. - pub async fn connect(&self, did: Did) -> Result { - if did == self.did() { + pub async fn connect(&self, peer: Did) -> Result<()> { + if peer == self.did() { return Err(Error::ShouldNotConnectSelf); } - JudgeConnection::connect(self, did).await + self.transport.connect(peer, self.inner_callback()?).await } - /// Similar to connect, but this function will try connect a Did by given hop. - pub async fn connect_via(&self, did: Did, next_hop: Did) -> Result { - if did == self.did() { - return Err(Error::ShouldNotConnectSelf); - } - JudgeConnection::connect_via(self, did, next_hop).await + /// Send [Message] to peer. + pub async fn send_message(&self, msg: Message, destination: Did) -> Result { + self.transport.send_message(msg, destination).await + } + + /// List peers and their connection status. + pub fn peers(&self) -> Vec { + self.transport + .get_connections() + .iter() + .map(|(did, c)| ConnectionInspect { + did: did.to_string(), + state: format!("{:?}", c.webrtc_connection_state()), + }) + .collect() } /// Check the status of swarm @@ -312,93 +120,72 @@ impl Swarm { } } -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl PayloadSender for Swarm { - fn session_sk(&self) -> &SessionSk { - Swarm::session_sk(self) - } - - fn dht(&self) -> Arc { - Swarm::dht(self) - } - - fn is_connected(&self, did: Did) -> bool { - let Some(conn) = self.get_connection(did) else { - return false; - }; - conn.webrtc_connection_state() == WebrtcConnectionState::Connected - } - - async fn do_send_payload(&self, did: Did, payload: MessagePayload) -> Result<()> { - let conn = self - .get_and_check_connection(did) - .await - .ok_or(Error::SwarmMissDidInTable(did))?; - - tracing::debug!( - "Try send {:?}, to node {:?}", - payload.clone(), - payload.relay.next_hop, - ); - - let data = payload.to_bincode()?; - if data.len() > TRANSPORT_MAX_SIZE { - tracing::error!("Message is too large: {:?}", payload); - return Err(Error::MessageTooLarge(data.len())); +impl Swarm { + /// Creaet new connection and its answer. This function will wrap the offer inside a payload + /// with verification. + pub async fn create_offer(&self, peer: Did) -> Result { + let offer_msg = self + .transport + .prepare_connection_offer(peer, self.inner_callback()?) + .await?; + + // This payload has fake next_hop. + // The invoker should fix it before sending. + let payload = MessagePayload::new_send( + Message::ConnectNodeSend(offer_msg), + self.transport.session_sk(), + self.did(), + peer, + )?; + + Ok(payload) + } + + /// Answer the offer of remote connection. This function will verify the answer payload and + /// will wrap the answer inside a payload with verification. + pub async fn answer_offer(&self, offer_payload: MessagePayload) -> Result { + if !offer_payload.verify() { + return Err(Error::VerifySignatureFailed); } - let result = if data.len() > TRANSPORT_MTU { - let chunks = ChunkList::::from(&data); - for chunk in chunks { - let data = - MessagePayload::new_send(Message::Chunk(chunk), &self.session_sk, did, did)? - .to_bincode()?; - conn.send_message(TransportMessage::Custom(data.to_vec())) - .await?; - } - Ok(()) - } else { - conn.send_message(TransportMessage::Custom(data.to_vec())) - .await + let Message::ConnectNodeSend(msg) = offer_payload.transaction.data()? else { + return Err(Error::InvalidMessage( + "Should be ConnectNodeSend".to_string(), + )); }; - tracing::debug!( - "Sent {:?}, to node {:?}", - payload.clone(), - payload.relay.next_hop, - ); - - if result.is_ok() { - self.record_sent(payload.relay.next_hop).await - } else { - self.record_sent_failed(payload.relay.next_hop).await - } - - result.map_err(|e| e.into()) - } -} - -#[cfg(not(feature = "wasm"))] -impl Swarm { - /// Listener for native envirement, It will just launch a loop. - pub async fn listen(self: Arc) { - loop { - self.listen_once().await; + let peer = offer_payload.transaction.signer(); + let answer_msg = self + .transport + .answer_remote_connection(peer, self.inner_callback()?, &msg) + .await?; + + // This payload has fake next_hop. + // The invoker should fix it before sending. + let answer_payload = MessagePayload::new_send( + Message::ConnectNodeReport(answer_msg), + self.transport.session_sk(), + self.did(), + self.did(), + )?; + + Ok(answer_payload) + } + + /// Accept the answer of remote connection. This function will verify the answer payload and + /// will return its did with the connection. + pub async fn accept_answer(&self, answer_payload: MessagePayload) -> Result<()> { + if !answer_payload.verify() { + return Err(Error::VerifySignatureFailed); } - } -} -#[cfg(feature = "wasm")] -impl Swarm { - /// Listener for browser envirement, the implementation is based on js_sys::window.set_timeout. - pub async fn listen(self: Arc) { - let func = move || { - let this = self.clone(); - wasm_bindgen_futures::spawn_local(Box::pin(async move { - this.listen_once().await; - })); + let Message::ConnectNodeReport(ref msg) = answer_payload.transaction.data()? else { + return Err(Error::InvalidMessage( + "Should be ConnectNodeReport".to_string(), + )); }; - crate::poll!(func, 10); + + let peer = answer_payload.transaction.signer(); + self.transport.accept_remote_connection(peer, msg).await } } diff --git a/crates/core/src/swarm/transport.rs b/crates/core/src/swarm/transport.rs new file mode 100644 index 000000000..cd86bf314 --- /dev/null +++ b/crates/core/src/swarm/transport.rs @@ -0,0 +1,345 @@ +use std::str::FromStr; +use std::sync::Arc; + +use async_trait::async_trait; +use bytes::Bytes; +use rings_transport::connection_ref::ConnectionRef; +#[cfg(feature = "dummy")] +pub use rings_transport::connections::DummyConnection as ConnectionOwner; +#[cfg(feature = "dummy")] +pub use rings_transport::connections::DummyTransport as Transport; +#[cfg(feature = "wasm")] +pub use rings_transport::connections::WebSysWebrtcConnection as ConnectionOwner; +#[cfg(feature = "wasm")] +pub use rings_transport::connections::WebSysWebrtcTransport as Transport; +#[cfg(all(not(feature = "wasm"), not(feature = "dummy")))] +use rings_transport::connections::WebrtcConnection as ConnectionOwner; +#[cfg(all(not(feature = "wasm"), not(feature = "dummy")))] +use rings_transport::connections::WebrtcTransport as Transport; +use rings_transport::core::transport::ConnectionInterface; +use rings_transport::core::transport::TransportInterface; +use rings_transport::core::transport::TransportMessage; +use rings_transport::core::transport::WebrtcConnectionState; + +use crate::chunk::ChunkList; +use crate::consts::TRANSPORT_MAX_SIZE; +use crate::consts::TRANSPORT_MTU; +use crate::dht::Did; +use crate::dht::LiveDid; +use crate::dht::PeerRing; +use crate::error::Error; +use crate::error::Result; +use crate::measure::MeasureImpl; +use crate::message::ConnectNodeReport; +use crate::message::ConnectNodeSend; +use crate::message::Message; +use crate::message::MessagePayload; +use crate::message::PayloadSender; +use crate::session::SessionSk; +use crate::swarm::callback::InnerSwarmCallback; + +pub struct SwarmTransport { + transport: Transport, + session_sk: SessionSk, + pub(crate) dht: Arc, + #[allow(dead_code)] + measure: Option, +} + +#[derive(Clone)] +pub struct SwarmConnection { + peer: Did, + pub connection: ConnectionRef, +} + +impl SwarmTransport { + pub fn new( + ice_servers: &str, + external_address: Option, + session_sk: SessionSk, + dht: Arc, + measure: Option, + ) -> Self { + Self { + transport: Transport::new(ice_servers, external_address), + session_sk, + dht, + measure, + } + } + + /// Create new connection that will be handled by swarm. + pub async fn new_connection(&self, peer: Did, callback: InnerSwarmCallback) -> Result<()> { + if peer == self.dht.did { + return Ok(()); + } + + let cid = peer.to_string(); + self.transport + .new_connection(&cid, Box::new(callback)) + .await + .map_err(Error::Transport) + } + + /// Get connection by did. + pub fn get_connection(&self, peer: Did) -> Option { + self.transport + .connection(&peer.to_string()) + .map(|conn| SwarmConnection { + peer, + connection: conn, + }) + .ok() + } + + /// Get all connections in transport. + pub fn get_connections(&self) -> Vec<(Did, SwarmConnection)> { + self.transport + .connections() + .into_iter() + .filter_map(|(k, v)| { + Did::from_str(&k).ok().map(|did| { + (did, SwarmConnection { + peer: did, + connection: v, + }) + }) + }) + .collect() + } + + /// Get dids of all connections in transport. + pub fn get_connection_ids(&self) -> Vec { + self.transport + .connection_ids() + .into_iter() + .filter_map(|k| Did::from_str(&k).ok()) + .collect() + } + + /// Disconnect a connection. There are three steps: + /// 1) remove from DHT; + /// 2) remove from Transport; + /// 3) close the connection; + pub async fn disconnect(&self, peer: Did) -> Result<()> { + tracing::info!("removing {peer} from DHT"); + self.dht.remove(peer)?; + self.transport + .close_connection(&peer.to_string()) + .await + .map_err(|e| e.into()) + } + + /// Connect a given Did. If the did is already connected, return Err, + /// else try prepare offer and establish connection by dht. + pub async fn connect(&self, peer: Did, callback: InnerSwarmCallback) -> Result<()> { + let offer_msg = self.prepare_connection_offer(peer, callback).await?; + self.send_message(Message::ConnectNodeSend(offer_msg), peer) + .await?; + Ok(()) + } + + /// Get connection by did and check if data channel is open. + /// This method will return None if the connection is not found. + /// This method will wait_for_data_channel_open. + /// If it's not ready in 8 seconds this method will close it and return None. + /// If it's ready in 8 seconds this method will return the connection. + /// See more information about [rings_transport::core::transport::WebrtcConnectionState]. + /// See also method webrtc_wait_for_data_channel_open [rings_transport::core::transport::ConnectionInterface]. + pub async fn get_and_check_connection(&self, peer: Did) -> Option { + let Some(conn) = self.get_connection(peer) else { + return None; + }; + + if let Err(e) = conn.connection.webrtc_wait_for_data_channel_open().await { + dbg!(&e); + tracing::warn!( + "[get_and_check_connection] connection {peer} data channel not open, will be dropped, reason: {e:?}" + ); + + if let Err(e) = self.disconnect(peer).await { + tracing::error!("Failed on close connection {peer}: {e:?}"); + } + + return None; + }; + + Some(conn) + } + + /// Create new connection and its offer. + pub async fn prepare_connection_offer( + &self, + peer: Did, + callback: InnerSwarmCallback, + ) -> Result { + if self.get_and_check_connection(peer).await.is_some() { + return Err(Error::AlreadyConnected); + }; + + self.new_connection(peer, callback).await?; + let conn = self + .transport + .connection(&peer.to_string()) + .map_err(Error::Transport)?; + + let offer = conn.webrtc_create_offer().await.map_err(Error::Transport)?; + let offer_str = serde_json::to_string(&offer).map_err(|_| Error::SerializeToString)?; + let offer_msg = ConnectNodeSend { sdp: offer_str }; + + Ok(offer_msg) + } + + /// Answer the offer of remote connection. + pub async fn answer_remote_connection( + &self, + peer: Did, + callback: InnerSwarmCallback, + offer_msg: &ConnectNodeSend, + ) -> Result { + let offer = serde_json::from_str(&offer_msg.sdp).map_err(Error::Deserialize)?; + + if let Some(swarm_conn) = self.get_connection(peer) { + // Solve the scenario of creating offers simultaneously. + // + // When both sides create_offer at the same time and trigger answer_offer of the other side, + // they will got existed New state connection when answer_offer, which will prevent + // it to create new connection to answer the offer. + // + // The party with a larger Did (ranked lower on the ring) should abandon their own offer and instead answer_offer to the other party. + // The party with a smaller Did should reject answering the other party and report an Error::AlreadyConnected error. + if swarm_conn.connection.webrtc_connection_state() == WebrtcConnectionState::New { + // drop local offer and continue answer remote offer + if self.dht.did > peer { + // this connection will replaced by new connection created bellow + self.disconnect(peer).await?; + } else { + // ignore remote offer, and refuse to answer remote offer + return Err(Error::AlreadyConnected); + } + } else if self.get_and_check_connection(peer).await.is_some() { + return Err(Error::AlreadyConnected); + }; + }; + + self.new_connection(peer, callback).await?; + let conn = self + .transport + .connection(&peer.to_string()) + .map_err(Error::Transport)?; + + let answer = conn + .webrtc_answer_offer(offer) + .await + .map_err(Error::Transport)?; + let answer_str = serde_json::to_string(&answer).map_err(|_| Error::SerializeToString)?; + let answer_msg = ConnectNodeReport { sdp: answer_str }; + + Ok(answer_msg) + } + + /// Accept the answer of remote connection. + pub async fn accept_remote_connection( + &self, + peer: Did, + answer_msg: &ConnectNodeReport, + ) -> Result<()> { + let answer = serde_json::from_str(&answer_msg.sdp).map_err(Error::Deserialize)?; + + let conn = self + .transport + .connection(&peer.to_string()) + .map_err(Error::Transport)?; + conn.webrtc_accept_answer(answer) + .await + .map_err(Error::Transport)?; + + Ok(()) + } +} + +impl SwarmConnection { + pub async fn send_data(&self, data: Bytes) -> Result<()> { + self.connection + .send_message(TransportMessage::Custom(data.to_vec())) + .await + .map_err(|e| e.into()) + } + + pub fn webrtc_connection_state(&self) -> WebrtcConnectionState { + self.connection.webrtc_connection_state() + } +} + +#[cfg_attr(feature = "wasm", async_trait(?Send))] +#[cfg_attr(not(feature = "wasm"), async_trait)] +impl PayloadSender for SwarmTransport { + fn session_sk(&self) -> &SessionSk { + &self.session_sk + } + + fn dht(&self) -> Arc { + self.dht.clone() + } + + fn is_connected(&self, did: Did) -> bool { + let Some(conn) = self.get_connection(did) else { + return false; + }; + conn.webrtc_connection_state() == WebrtcConnectionState::Connected + } + + async fn do_send_payload(&self, did: Did, payload: MessagePayload) -> Result<()> { + let conn = self + .get_and_check_connection(did) + .await + .ok_or(Error::SwarmMissDidInTable(did))?; + + tracing::debug!( + "Try send {:?}, to node {:?}", + payload.clone(), + payload.relay.next_hop, + ); + + let data = payload.to_bincode()?; + if data.len() > TRANSPORT_MAX_SIZE { + tracing::error!("Message is too large: {:?}", payload); + return Err(Error::MessageTooLarge(data.len())); + } + + let result = if data.len() > TRANSPORT_MTU { + let chunks = ChunkList::::from(&data); + for chunk in chunks { + let data = + MessagePayload::new_send(Message::Chunk(chunk), &self.session_sk, did, did)? + .to_bincode()?; + conn.send_data(data).await?; + } + Ok(()) + } else { + conn.send_data(data).await + }; + + tracing::debug!( + "Sent {:?}, to node {:?}", + payload.clone(), + payload.relay.next_hop, + ); + + result + } +} + +#[cfg_attr(feature = "wasm", async_trait(?Send))] +#[cfg_attr(not(feature = "wasm"), async_trait)] +impl LiveDid for SwarmConnection { + async fn live(&self) -> bool { + self.webrtc_connection_state() == WebrtcConnectionState::Connected + } +} + +impl From for Did { + fn from(conn: SwarmConnection) -> Self { + conn.peer + } +} diff --git a/crates/core/src/swarm/types.rs b/crates/core/src/swarm/types.rs deleted file mode 100644 index aef00a875..000000000 --- a/crates/core/src/swarm/types.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![warn(missing_docs)] -//! This module defines type and type alias related to Swarm. -use async_trait::async_trait; -use rings_transport::core::transport::ConnectionInterface; - -use crate::dht::Did; -use crate::dht::LiveDid; -use crate::measure::BehaviourJudgement; -use crate::swarm::Swarm; -use crate::types::Connection; - -/// Type of Measure, see [crate::measure::Measure]. -#[cfg(not(feature = "wasm"))] -pub type MeasureImpl = Box; - -/// Type of Measure, see [crate::measure::Measure]. -#[cfg(feature = "wasm")] -pub type MeasureImpl = Box; - -/// WrappedDid is a DID wrapped by Swarm and bound to a Connection, -/// which enables checking whether the WrappedDid is live or not. -#[derive(Clone)] -pub struct WrappedDid { - did: Did, - connection: Option, -} - -impl WrappedDid { - /// Creates a new WrappedDid using the provided Swarm instance and DID. - pub fn new(swarm: &Swarm, did: Did) -> Self { - // Try to get the Connection for the provided DID from the Swarm. - match swarm.transport.connection(&did.to_string()) { - Ok(c) => Self { - did, - // If the Transport is found, create a arc reference to it and store it in the WrappedDid. - // TODO: Should use weak reference - connection: Some(c), - }, - Err(_) => Self { - did, - connection: None, - }, - } - } -} - -impl From for Did { - /// Implements the From trait to allow conversion from WrappedDid to Did. - fn from(node: WrappedDid) -> Did { - node.did - } -} - -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -impl LiveDid for WrappedDid { - /// If the Transport is present and connected, returns true. - async fn live(&self) -> bool { - match &self.connection { - Some(c) => c.is_connected().await, - None => false, - } - } -} diff --git a/crates/core/src/tests/default/mod.rs b/crates/core/src/tests/default/mod.rs index 109ab1200..a414dcea4 100644 --- a/crates/core/src/tests/default/mod.rs +++ b/crates/core/src/tests/default/mod.rs @@ -1,11 +1,21 @@ use std::sync::Arc; +use async_trait::async_trait; +use dashmap::DashMap; +use futures::lock::Mutex; +use tokio::sync::mpsc; +use tokio::time::sleep; +use tokio::time::Duration; + use crate::dht::Did; use crate::dht::PeerRing; use crate::ecc::SecretKey; -use crate::error::Result; +use crate::message::Message; +use crate::message::MessagePayload; +use crate::message::MessageVerificationExt; use crate::session::SessionSk; use crate::storage::MemStorage; +use crate::swarm::callback::SwarmCallback; use crate::swarm::Swarm; use crate::swarm::SwarmBuilder; @@ -13,7 +23,70 @@ mod test_connection; mod test_message_handler; mod test_stabilization; -pub async fn prepare_node(key: SecretKey) -> Arc { +pub struct Node { + pub swarm: Arc, + message_rx: Mutex>, +} + +pub struct NodeCallback { + message_tx: mpsc::UnboundedSender, +} + +impl Node { + pub fn new(swarm: Arc) -> Self { + let (message_tx, message_rx) = mpsc::unbounded_channel(); + let callback = NodeCallback { message_tx }; + swarm.set_callback(Arc::new(callback)).unwrap(); + Self { + swarm, + message_rx: Mutex::new(message_rx), + } + } + + pub async fn listen_once(&self) -> Option { + self.message_rx.lock().await.recv().await + } + + pub fn did(&self) -> Did { + self.swarm.did() + } + + pub fn dht(&self) -> Arc { + self.swarm.dht().clone() + } + + pub fn assert_transports(&self, addresses: Vec) { + println!( + "Check transport of {:?}: {:?} for addresses {:?}", + self.did(), + self.swarm.transport.get_connection_ids(), + addresses + ); + assert_eq!( + self.swarm.transport.get_connections().len(), + addresses.len() + ); + for addr in addresses { + assert!(self.swarm.transport.get_connection(addr).is_some()); + } + } +} + +#[async_trait] +impl SwarmCallback for NodeCallback { + async fn on_validate( + &self, + payload: &MessagePayload, + ) -> Result<(), Box> { + // Here we are using on_validate to record messages. + // When on_validate return error, the message will be ignored, which is not on purpose. + // To prevent returning errors when sending fails, we choose to panic instead. + self.message_tx.send(payload.clone()).unwrap(); + Ok(()) + } +} + +pub async fn prepare_node(key: SecretKey) -> Node { let stun = "stun://stun.l.google.com:19302"; let storage = Box::new(MemStorage::new()); @@ -23,15 +96,15 @@ pub async fn prepare_node(key: SecretKey) -> Arc { println!("key: {:?}", key.to_string()); println!("did: {:?}", swarm.did()); - swarm + Node::new(swarm) } -pub async fn gen_pure_dht(did: Did) -> Result { +pub fn gen_pure_dht(did: Did) -> PeerRing { let storage = Box::new(MemStorage::new()); - Ok(PeerRing::new_with_storage(did, 3, storage)) + PeerRing::new_with_storage(did, 3, storage) } -pub async fn gen_sorted_dht(s: usize) -> Vec { +pub fn gen_sorted_dht(s: usize) -> Vec { let mut keys: Vec = vec![]; for _i in 0..s { keys.push(crate::ecc::SecretKey::random()); @@ -47,11 +120,76 @@ pub async fn gen_sorted_dht(s: usize) -> Vec { let mut iter = dids.into_iter(); let mut ret: Vec = vec![]; for _ in 0..s { - ret.push( - crate::tests::default::gen_pure_dht(iter.next().unwrap()) - .await - .unwrap(), - ) + ret.push(crate::tests::default::gen_pure_dht(iter.next().unwrap())) } ret } + +pub async fn assert_no_more_msg(node1: &Node, node2: &Node, node3: &Node) { + tokio::select! { + _ = node1.listen_once() => unreachable!("node1 should not receive any message"), + _ = node2.listen_once() => unreachable!("node2 should not receive any message"), + _ = node3.listen_once() => unreachable!("node3 should not receive any message"), + _ = sleep(Duration::from_secs(3)) => {} + } +} + +pub async fn wait_for_msgs(node1: &Node, node2: &Node, node3: &Node) { + let did_names: DashMap = DashMap::new(); + did_names.insert(node1.did(), "node1"); + did_names.insert(node2.did(), "node2"); + did_names.insert(node3.did(), "node3"); + + let listen1 = async { + loop { + tokio::select! { + Some(payload) = node1.listen_once() => { + println!( + "Msg {} -> node1 [{} => {}] : {:?}", + *did_names.get(&payload.signer()).unwrap(), + *did_names.get(&payload.transaction.signer()).unwrap(), + *did_names.get(&payload.transaction.destination).unwrap(), + payload.transaction.data::().unwrap() + ) + } + _ = sleep(Duration::from_secs(3)) => break + } + } + }; + + let listen2 = async { + loop { + tokio::select! { + Some(payload) = node2.listen_once() => { + println!( + "Msg {} -> node2 [{} => {}] : {:?}", + *did_names.get(&payload.signer()).unwrap(), + *did_names.get(&payload.transaction.signer()).unwrap(), + *did_names.get(&payload.transaction.destination).unwrap(), + payload.transaction.data::().unwrap() + ) + } + _ = sleep(Duration::from_secs(3)) => break + } + } + }; + + let listen3 = async { + loop { + tokio::select! { + Some(payload) = node3.listen_once() => { + println!( + "Msg {} -> node3 [{} => {}] : {:?}", + *did_names.get(&payload.signer()).unwrap(), + *did_names.get(&payload.transaction.signer()).unwrap(), + *did_names.get(&payload.transaction.destination).unwrap(), + payload.transaction.data::().unwrap() + ) + } + _ = sleep(Duration::from_secs(3)) => break + } + } + }; + + futures::join!(listen1, listen2, listen3); +} diff --git a/crates/core/src/tests/default/test_connection.rs b/crates/core/src/tests/default/test_connection.rs index 9035924e3..7de789bbb 100644 --- a/crates/core/src/tests/default/test_connection.rs +++ b/crates/core/src/tests/default/test_connection.rs @@ -1,57 +1,73 @@ -use rings_transport::core::transport::ConnectionInterface; use rings_transport::core::transport::WebrtcConnectionState; +use crate::ecc::tests::gen_ordered_keys; use crate::ecc::SecretKey; -use crate::error::Result; -use crate::message::handlers::tests::assert_no_more_msg; -use crate::message::handlers::tests::wait_for_msgs; +use crate::tests::default::assert_no_more_msg; use crate::tests::default::prepare_node; +use crate::tests::default::wait_for_msgs; use crate::tests::manually_establish_connection; #[tokio::test] -async fn test_handshake_on_both_sides() -> Result<()> { - let key1 = SecretKey::random(); - let key2 = SecretKey::random(); - let key3 = SecretKey::random(); +async fn test_handshake_on_both_sides_ordered() { + let keys = gen_ordered_keys(3); + let (key1, key2, key3) = (keys[0], keys[1], keys[2]); + test_handshake_on_both_sides(key1, key2, key3).await +} + +#[tokio::test] +async fn test_handshake_on_both_sides_desc_ordered() { + let keys = gen_ordered_keys(3); + let (key3, key2, key1) = (keys[0], keys[1], keys[2]); + test_handshake_on_both_sides(key1, key2, key3).await +} - let swarm1 = prepare_node(key1).await; - let swarm2 = prepare_node(key2).await; - let swarm3 = prepare_node(key3).await; +async fn test_handshake_on_both_sides(key1: SecretKey, key2: SecretKey, key3: SecretKey) { + let node1 = prepare_node(key1).await; + let node2 = prepare_node(key2).await; + let node3 = prepare_node(key3).await; - assert!(swarm1.get_connection(swarm2.did()).is_none()); - assert!(swarm2.get_connection(swarm1.did()).is_none()); + assert!(node1.swarm.transport.get_connection(node2.did()).is_none()); + assert!(node2.swarm.transport.get_connection(node1.did()).is_none()); // connect to middle peer - manually_establish_connection(&swarm1, &swarm3).await; - manually_establish_connection(&swarm2, &swarm3).await; - wait_for_msgs(&swarm1, &swarm2, &swarm3).await; - assert_no_more_msg(&swarm1, &swarm2, &swarm3).await; + manually_establish_connection(&node1.swarm, &node3.swarm).await; + manually_establish_connection(&node2.swarm, &node3.swarm).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; assert_eq!( - swarm3 - .get_connection(swarm1.did()) + node3 + .swarm + .transport + .get_connection(node1.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected ); assert_eq!( - swarm3 - .get_connection(swarm2.did()) + node3 + .swarm + .transport + .get_connection(node2.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected ); assert_eq!( - swarm1 - .get_connection(swarm3.did()) + node1 + .swarm + .transport + .get_connection(node3.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected ); assert_eq!( - swarm2 - .get_connection(swarm3.did()) + node2 + .swarm + .transport + .get_connection(node3.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected @@ -60,48 +76,54 @@ async fn test_handshake_on_both_sides() -> Result<()> { // connect to each at same time // Node 1 -> Offer -> Node 2 // Node 2 -> Offer -> Node 1 - swarm1.connect(swarm2.did()).await.unwrap(); - swarm2.connect(swarm1.did()).await.unwrap(); + _ = node1.swarm.connect(node2.did()).await; + _ = node2.swarm.connect(node1.did()).await; // The conn state of swarm1 -> swarm2 is new assert_eq!( - swarm1 - .get_connection(swarm2.did()) + node1 + .swarm + .transport + .get_connection(node2.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::New, ); // The conn state of swarm2 -> swarm1 is new assert_eq!( - swarm2 - .get_connection(swarm1.did()) + node2 + .swarm + .transport + .get_connection(node1.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::New, ); - wait_for_msgs(&swarm1, &swarm2, &swarm3).await; - assert_no_more_msg(&swarm1, &swarm2, &swarm3).await; + wait_for_msgs(&node1, &node2, &node3).await; + assert_no_more_msg(&node1, &node2, &node3).await; // When node 1 got offer, node 1 may accept offer if did 1 < did 2, drop local connection // and response answer // When node 2 got offer, node 2 reject offer if did 1 < did 2 assert_eq!( - swarm1 - .get_connection(swarm2.did()) + node1 + .swarm + .transport + .get_connection(node2.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected, ); assert_eq!( - swarm2 - .get_connection(swarm1.did()) + node2 + .swarm + .transport + .get_connection(node1.did()) .unwrap() .webrtc_connection_state(), WebrtcConnectionState::Connected, - ); - - Ok(()) + ) } diff --git a/crates/core/src/tests/default/test_message_handler.rs b/crates/core/src/tests/default/test_message_handler.rs index 9d507a9ad..dede4f6a2 100644 --- a/crates/core/src/tests/default/test_message_handler.rs +++ b/crates/core/src/tests/default/test_message_handler.rs @@ -1,6 +1,5 @@ use std::str::FromStr; -use rings_transport::core::transport::ConnectionInterface; use rings_transport::core::transport::WebrtcConnectionState; use tokio::time::sleep; use tokio::time::Duration; @@ -9,14 +8,12 @@ use crate::dht::successor::SuccessorReader; use crate::dht::vnode::VirtualNode; use crate::ecc::tests::gen_ordered_keys; use crate::ecc::SecretKey; -use crate::error::Error; use crate::error::Result; use crate::message; use crate::message::Encoder; use crate::message::FindSuccessorReportHandler; use crate::message::FindSuccessorThen; use crate::message::Message; -use crate::message::PayloadSender; use crate::prelude::vnode::VNodeOperation; use crate::tests::default::prepare_node; use crate::tests::manually_establish_connection; @@ -27,7 +24,7 @@ async fn test_handle_join() -> Result<()> { let key2 = SecretKey::random(); let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - manually_establish_connection(&node1, &node2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; assert!(node1.listen_once().await.is_some()); assert!(node1 .dht() @@ -47,93 +44,79 @@ async fn test_handle_connect_node() -> Result<()> { let node3 = prepare_node(key3).await; // 2 to 3 - manually_establish_connection(&node3, &node2).await; + manually_establish_connection(&node3.swarm, &node2.swarm).await; // 1 to 2 - manually_establish_connection(&node1, &node2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; sleep(Duration::from_secs(3)).await; - tokio::select! { - _ = async { - futures::join!( - async { node1.clone().listen().await }, - async { node2.clone().listen().await }, - async { node3.clone().listen().await }, - ) - } => {unreachable!();} - _ = async { - // handle join dht situation - println!("wait connection 1 to 2 and connection 2 to 3 connected"); - sleep(Duration::from_millis(1)).await; - let connection_1_to_2 = node1.get_connection(node2.did()).unwrap(); - let connection_2_to_3 = node2.get_connection(node3.did()).unwrap(); + // handle join dht situation + println!("wait connection 1 to 2 and connection 2 to 3 connected"); + sleep(Duration::from_millis(1)).await; + let connection_1_to_2 = node1.swarm.transport.get_connection(node2.did()).unwrap(); + let connection_2_to_3 = node2.swarm.transport.get_connection(node3.did()).unwrap(); - println!("wait events trigger"); - sleep(Duration::from_millis(1)).await; + println!("wait events trigger"); + sleep(Duration::from_millis(1)).await; - println!("node1 key address: {:?}", node1.did()); - println!("node2 key address: {:?}", node2.did()); - println!("node3 key address: {:?}", node3.did()); - let dht1 = node1.dht(); - let dht2 = node2.dht(); - let dht3 = node3.dht(); - { - let dht1_successor = dht1.successors(); - let dht2_successor = dht2.successors(); - let dht3_successor = dht3.successors(); - println!("node1.dht() successor: {:?}", dht1_successor); - println!("node2.dht() successor: {:?}", dht2_successor); - println!("node3.dht() successor: {:?}", dht3_successor); + println!("node1 key address: {:?}", node1.did()); + println!("node2 key address: {:?}", node2.did()); + println!("node3 key address: {:?}", node3.did()); + let dht1 = node1.dht(); + let dht2 = node2.dht(); + let dht3 = node3.dht(); + { + let dht1_successor = dht1.successors(); + let dht2_successor = dht2.successors(); + let dht3_successor = dht3.successors(); + println!("node1.dht() successor: {:?}", dht1_successor); + println!("node2.dht() successor: {:?}", dht2_successor); + println!("node3.dht() successor: {:?}", dht3_successor); - assert!( - dht1_successor.list()?.contains( - &key2.address().into() - ), - "Expect node1.dht() successor is key2, Found: {:?}", - dht1_successor.list()? - ); - assert!( - dht2_successor.list()?.contains( - &key3.address().into() - ), "{:?}", dht2_successor.list()); - assert!( - dht3_successor.list()?.contains( - &key2.address().into() - ), - "node3.dht() successor is key2" - ); - } + assert!( + dht1_successor.list()?.contains(&key2.address().into()), + "Expect node1.dht() successor is key2, Found: {:?}", + dht1_successor.list()? + ); + assert!( + dht2_successor.list()?.contains(&key3.address().into()), + "{:?}", + dht2_successor.list() + ); + assert!( + dht3_successor.list()?.contains(&key2.address().into()), + "node3.dht() successor is key2" + ); + } - assert_eq!( - connection_1_to_2.ice_connection_state(), - WebrtcConnectionState::Connected, - ); - assert_eq!( - connection_2_to_3.ice_connection_state(), - WebrtcConnectionState::Connected, - ); + assert_eq!( + connection_1_to_2.webrtc_connection_state(), + WebrtcConnectionState::Connected, + ); + assert_eq!( + connection_2_to_3.webrtc_connection_state(), + WebrtcConnectionState::Connected, + ); - // node1.dht() send msg to node2.dht() ask for connecting node3.dht() - node1.connect(node3.did()).await.unwrap(); - sleep(Duration::from_millis(10000)).await; + // node1.dht() send msg to node2.dht() ask for connecting node3.dht() + node1.swarm.connect(node3.did()).await.unwrap(); + sleep(Duration::from_millis(10000)).await; + + let connection_1_to_3 = node1.swarm.transport.get_connection(node3.did()); + assert!(connection_1_to_3.is_some()); + let connection_1_to_3 = connection_1_to_3.unwrap(); + let both = { + connection_1_to_3.webrtc_connection_state() == WebrtcConnectionState::New + || connection_1_to_3.webrtc_connection_state() == WebrtcConnectionState::Connecting + || connection_1_to_3.webrtc_connection_state() == WebrtcConnectionState::Connected + }; + assert!(both, "{:?}", connection_1_to_3.webrtc_connection_state()); + assert_eq!( + connection_1_to_3.webrtc_connection_state(), + WebrtcConnectionState::Connected + ); - let connection_1_to_3 = node1.get_connection(node3.did()); - assert!(connection_1_to_3.is_some()); - let connection_1_to_3 = connection_1_to_3.unwrap(); - let both = { - connection_1_to_3.ice_connection_state() == WebrtcConnectionState::New || - connection_1_to_3.ice_connection_state() == WebrtcConnectionState::Connecting || - connection_1_to_3.ice_connection_state() == WebrtcConnectionState::Connected - }; - assert!(both, "{:?}", connection_1_to_3.ice_connection_state()); - assert_eq!( - connection_1_to_3.ice_connection_state(), - WebrtcConnectionState::Connected - ); - Ok::<(), Error>(()) - } => {} - } Ok(()) } @@ -143,45 +126,44 @@ async fn test_handle_notify_predecessor() -> Result<()> { let key2 = SecretKey::random(); let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - manually_establish_connection(&node1, &node2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; - // handle join dht situation - tokio::select! { - _ = async { - futures::join!( - async { - loop { - node1.clone().listen().await; - } - }, - async { - loop { - node2.clone().listen().await; - } - } - ); - } => { unreachable!();} - _ = async { - let connection_1_to_2 = node1.get_connection(node2.did()).unwrap(); - sleep(Duration::from_millis(1000)).await; - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); - assert!(node2.dht().successors().list()?.contains(&key1.address().into())); - assert_eq!(connection_1_to_2.ice_connection_state(), WebrtcConnectionState::Connected); - node1 - .send_message( - Message::NotifyPredecessorSend(message::NotifyPredecessorSend { - did: key1.address().into(), - }), - node2.did(), - ) - .await - .unwrap(); - sleep(Duration::from_millis(1000)).await; - assert_eq!(*node2.dht().lock_predecessor()?, Some(key1.address().into())); - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); - Ok::<(), Error>(()) - } => {} - } + let connection_1_to_2 = node1.swarm.transport.get_connection(node2.did()).unwrap(); + sleep(Duration::from_millis(1000)).await; + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); + assert!(node2 + .dht() + .successors() + .list()? + .contains(&key1.address().into())); + assert_eq!( + connection_1_to_2.webrtc_connection_state(), + WebrtcConnectionState::Connected + ); + node1 + .swarm + .send_message( + Message::NotifyPredecessorSend(message::NotifyPredecessorSend { + did: key1.address().into(), + }), + node2.did(), + ) + .await + .unwrap(); + sleep(Duration::from_millis(1000)).await; + assert_eq!( + *node2.dht().lock_predecessor()?, + Some(key1.address().into()) + ); + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); Ok(()) } @@ -195,64 +177,72 @@ async fn test_handle_find_successor_increase() -> Result<()> { } let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - manually_establish_connection(&node1, &node2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + + let connection_1_to_2 = node1.swarm.transport.get_connection(node2.did()).unwrap(); + sleep(Duration::from_millis(1000)).await; + assert!( + node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into()), + "{:?}", + node1.dht().successors().list()? + ); + assert!(node2 + .dht() + .successors() + .list()? + .contains(&key1.address().into())); + assert_eq!( + connection_1_to_2.webrtc_connection_state(), + WebrtcConnectionState::Connected + ); + node1 + .swarm + .send_message( + Message::NotifyPredecessorSend(message::NotifyPredecessorSend { did: node1.did() }), + node2.did(), + ) + .await + .unwrap(); + sleep(Duration::from_millis(1000)).await; + assert_eq!( + *node2.dht().lock_predecessor()?, + Some(key1.address().into()) + ); + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); - tokio::select! { - _ = async { - futures::join!( - async { - loop { - node1.clone().listen().await; - } - }, - async { - loop { - node2.clone().listen().await; - } - } - ); - } => { unreachable!();} - _ = async { - let connection_1_to_2 = node1.get_connection(node2.did()).unwrap(); - sleep(Duration::from_millis(1000)).await; - assert!(node1.dht().successors().list()?.contains(&key2.address().into()), "{:?}", node1.dht().successors().list()?); - assert!(node2.dht().successors().list()?.contains(&key1.address().into())); - assert_eq!(connection_1_to_2.ice_connection_state(), WebrtcConnectionState::Connected); - node1 - .send_message( - Message::NotifyPredecessorSend(message::NotifyPredecessorSend { - did: node1.did(), - }), - node2.did(), - ) - .await - .unwrap(); - sleep(Duration::from_millis(1000)).await; - assert_eq!(*node2.dht().lock_predecessor()?, Some(key1.address().into())); - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); + println!("node1: {:?}, node2: {:?}", node1.did(), node2.did()); + node2 + .swarm + .send_message( + Message::FindSuccessorSend(message::FindSuccessorSend { + did: node2.did(), + then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), + strict: true, + }), + node1.did(), + ) + .await + .unwrap(); + sleep(Duration::from_millis(1000)).await; + assert!(node2 + .dht() + .successors() + .list()? + .contains(&key1.address().into())); + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); - println!( - "node1: {:?}, node2: {:?}", - node1.did(), - node2.did() - ); - node2 - .send_message( - Message::FindSuccessorSend(message::FindSuccessorSend { - did: node2.did(), - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - strict: true - }), - node1.did(), - ) - .await - .unwrap(); - sleep(Duration::from_millis(1000)).await; - assert!(node2.dht().successors().list()?.contains(&key1.address().into())); - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); - Ok::<(), Error>(()) - } => {} - } Ok(()) } @@ -266,72 +256,69 @@ async fn test_handle_find_successor_decrease() -> Result<()> { } let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - manually_establish_connection(&node1, &node2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + + let connection_1_to_2 = node1.swarm.transport.get_connection(node2.did()).unwrap(); + sleep(Duration::from_millis(1000)).await; + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); + assert!(node2 + .dht() + .successors() + .list()? + .contains(&key1.address().into())); + assert!(node1 + .dht() + .lock_finger()? + .contains(Some(key2.address().into()))); + assert!(node2 + .dht() + .lock_finger()? + .contains(Some(key1.address().into()))); + assert_eq!( + connection_1_to_2.webrtc_connection_state(), + WebrtcConnectionState::Connected + ); + node1 + .swarm + .send_message( + Message::NotifyPredecessorSend(message::NotifyPredecessorSend { did: node1.did() }), + node2.did(), + ) + .await + .unwrap(); + sleep(Duration::from_millis(1000)).await; + assert_eq!( + *node2.dht().lock_predecessor()?, + Some(key1.address().into()) + ); + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); + println!("node1: {:?}, node2: {:?}", node1.did(), node2.did()); + node2 + .swarm + .send_message( + Message::FindSuccessorSend(message::FindSuccessorSend { + did: node2.did(), + then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), + strict: true, + }), + node1.did(), + ) + .await + .unwrap(); + sleep(Duration::from_millis(1000)).await; + let dht1_successor = node1.dht().successors(); + let dht2_successor = node2.dht().successors(); + assert!(dht2_successor.list()?.contains(&key1.address().into())); + assert!(dht1_successor.list()?.contains(&key2.address().into())); - // handle join dht situation - tokio::select! { - _ = async { - futures::join!( - async { - loop { - node1.clone().listen().await; - } - }, - async { - loop { - node2.clone().listen().await; - } - } - ); - } => {unreachable!();} - _ = async { - let connection_1_to_2 = node1.get_connection(node2.did()).unwrap(); - sleep(Duration::from_millis(1000)).await; - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); - assert!(node2.dht().successors().list()?.contains(&key1.address().into())); - assert!(node1.dht() - .lock_finger()? - .contains(Some(key2.address().into()))); - assert!(node2.dht() - .lock_finger()? - .contains(Some(key1.address().into()))); - assert_eq!(connection_1_to_2.ice_connection_state(), WebrtcConnectionState::Connected); - node1 - .send_message( - Message::NotifyPredecessorSend(message::NotifyPredecessorSend { - did: node1.did(), - }), - node2.did(), - ) - .await - .unwrap(); - sleep(Duration::from_millis(1000)).await; - assert_eq!(*node2.dht().lock_predecessor()?, Some(key1.address().into())); - assert!(node1.dht().successors().list()?.contains(&key2.address().into())); - println!( - "node1: {:?}, node2: {:?}", - node1.did(), - node2.did() - ); - node2 - .send_message( - Message::FindSuccessorSend(message::FindSuccessorSend { - did: node2.did(), - then: FindSuccessorThen::Report(FindSuccessorReportHandler::Connect), - strict: true - }), - node1.did(), - ) - .await - .unwrap(); - sleep(Duration::from_millis(1000)).await; - let dht1_successor = node1.dht().successors(); - let dht2_successor = node2.dht().successors(); - assert!(dht2_successor.list()?.contains(&key1.address().into())); - assert!(dht1_successor.list()?.contains(&key2.address().into())); - Ok::<(), Error>(()) - } => {} - }; Ok(()) } @@ -352,14 +339,9 @@ async fn test_handle_storage() -> Result<()> { ); let node1 = prepare_node(key1).await; let node2 = prepare_node(key2).await; - manually_establish_connection(&node1, &node2).await; - - let n1 = node1.clone(); - let n2 = node2.clone(); - tokio::spawn(async move { n1.listen().await }); - tokio::spawn(async move { n2.listen().await }); + manually_establish_connection(&node1.swarm, &node2.swarm).await; - let connection_1_to_2 = node1.get_connection(node2.did()).unwrap(); + let connection_1_to_2 = node1.swarm.transport.get_connection(node2.did()).unwrap(); sleep(Duration::from_millis(1000)).await; // node1's successor is node2 // node2's successor is node1 @@ -374,10 +356,11 @@ async fn test_handle_storage() -> Result<()> { .list()? .contains(&key1.address().into())); assert_eq!( - connection_1_to_2.ice_connection_state(), + connection_1_to_2.webrtc_connection_state(), WebrtcConnectionState::Connected ); node1 + .swarm .send_message( Message::NotifyPredecessorSend(message::NotifyPredecessorSend { did: node1.did() }), node2.did(), @@ -401,6 +384,7 @@ async fn test_handle_storage() -> Result<()> { // the vid is hash of string let vnode: VirtualNode = (message.clone(), encoded_message).try_into().unwrap(); node1 + .swarm .send_message( Message::OperateVNode(VNodeOperation::Overwrite(vnode.clone())), node2.did(), diff --git a/crates/core/src/tests/default/test_stabilization.rs b/crates/core/src/tests/default/test_stabilization.rs index bee3a9608..50beb9a2e 100644 --- a/crates/core/src/tests/default/test_stabilization.rs +++ b/crates/core/src/tests/default/test_stabilization.rs @@ -5,45 +5,15 @@ use tokio::time::sleep; use crate::dht::successor::SuccessorReader; use crate::dht::Chord; -use crate::dht::Stabilization; -use crate::dht::TStabilize; use crate::ecc::SecretKey; use crate::error::Error; use crate::error::Result; use crate::inspect::DHTInspect; use crate::inspect::SwarmInspect; -use crate::swarm::Swarm; use crate::tests::default::gen_pure_dht; use crate::tests::default::prepare_node; use crate::tests::manually_establish_connection; -async fn run_stabilize(swarm: Arc) { - let mut result = Result::<()>::Ok(()); - let stabilization = Stabilization::new(swarm, 5); - let timeout_in_secs = stabilization.get_timeout(); - println!("RUN Stabilization"); - while result.is_ok() { - let timeout = sleep(Duration::from_secs(timeout_in_secs)); - tokio::pin!(timeout); - tokio::select! { - _ = timeout.as_mut() => { - result = stabilization - .stabilize() - .await; - } - } - } -} - -async fn run_node(swarm: Arc) { - let message_handler = async { swarm.clone().listen().await }; - - let stb = Stabilization::new(swarm.clone(), 3); - let stabilization = async { Arc::new(stb).wait().await }; - - futures::future::join(message_handler, stabilization).await; -} - #[tokio::test] async fn test_stabilization_once() -> Result<()> { let mut key1 = SecretKey::random(); @@ -52,35 +22,36 @@ async fn test_stabilization_once() -> Result<()> { if key1.address() < key2.address() { (key1, key2) = (key2, key1) } - let swarm1 = prepare_node(key1).await; - let swarm2 = prepare_node(key2).await; - manually_establish_connection(&swarm1, &swarm2).await; - println!("swarm1: {:?}, swarm2: {:?}", swarm1.did(), swarm2.did()); + let node1 = prepare_node(key1).await; + let node2 = prepare_node(key2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; + println!("swarm1: {:?}, swarm2: {:?}", node1.did(), node2.did()); + + sleep(Duration::from_millis(1000)).await; + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); + assert!(node2 + .dht() + .successors() + .list()? + .contains(&key1.address().into())); + + let stabilizer = node1.swarm.stabilizer(); + let _ = stabilizer.stabilize().await; + sleep(Duration::from_millis(10000)).await; + assert_eq!( + *node2.dht().lock_predecessor()?, + Some(key1.address().into()) + ); + assert!(node1 + .dht() + .successors() + .list()? + .contains(&key2.address().into())); - tokio::select! { - _ = async { - futures::join!( - async { - swarm1.clone().listen().await; - }, - async { - swarm2.clone().listen().await; - }, - ); - } => { unreachable!(); } - _ = async { - sleep(Duration::from_millis(1000)).await; - assert!(swarm1.dht().successors().list()?.contains(&key2.address().into())); - assert!(swarm2.dht().successors().list()?.contains(&key1.address().into())); - let stabilization = Stabilization::new(Arc::clone(&swarm1), 5); - let _ = stabilization.stabilize().await; - sleep(Duration::from_millis(10000)).await; - assert_eq!(*swarm2.dht().lock_predecessor()?, Some(key1.address().into())); - assert!(swarm1.dht().successors().list()?.contains(&key2.address().into())); - - Ok::<(), Error>(()) - } => {} - } Ok(()) } @@ -92,34 +63,30 @@ async fn test_stabilization() -> Result<()> { if key1.address() < key2.address() { (key1, key2) = (key2, key1) } - let swarm1 = prepare_node(key1).await; - let swarm2 = prepare_node(key2).await; - manually_establish_connection(&swarm1, &swarm2).await; + let node1 = prepare_node(key1).await; + let node2 = prepare_node(key2).await; + manually_establish_connection(&node1.swarm, &node2.swarm).await; tokio::select! { _ = async { tokio::join!( async { - swarm1.clone().listen().await; - }, - async { - swarm2.clone().listen().await; + let stabilizer = Arc::new(node1.swarm.stabilizer()); + stabilizer.wait(Duration::from_secs(5)).await; }, async { - run_stabilize(Arc::clone(&swarm1)).await; - }, - async { - run_stabilize(Arc::clone(&swarm2)).await; + let stabilizer = Arc::new(node2.swarm.stabilizer()); + stabilizer.wait(Duration::from_secs(5)).await; } ); } => { unreachable!(); } _ = async { sleep(Duration::from_millis(1000)).await; - assert!(swarm1.dht().successors().list()?.contains(&key2.address().into())); - assert!(swarm2.dht().successors().list()?.contains(&key1.address().into())); + assert!(node1.dht().successors().list()?.contains(&key2.address().into())); + assert!(node2.dht().successors().list()?.contains(&key1.address().into())); sleep(Duration::from_millis(10000)).await; - assert_eq!(*swarm2.dht().lock_predecessor()?, Some(key1.address().into())); - assert_eq!(*swarm1.dht().lock_predecessor()?, Some(key2.address().into())); + assert_eq!(*node2.dht().lock_predecessor()?, Some(key1.address().into())); + assert_eq!(*node1.dht().lock_predecessor()?, Some(key2.address().into())); Ok::<(), Error>(()) } => {} } @@ -128,6 +95,10 @@ async fn test_stabilization() -> Result<()> { #[tokio::test] async fn test_stabilization_final_dht() -> Result<()> { + let mut swarms = vec![]; + + // Save nodes to prevent the receiver from being lost, + // which would lead to a panic in the monitoring callback when recording messages. let mut nodes = vec![]; let keys = vec![ @@ -138,22 +109,24 @@ async fn test_stabilization_final_dht() -> Result<()> { for s in keys { let key = SecretKey::try_from(s).unwrap(); - let swarm = prepare_node(key).await; - nodes.push(swarm.clone()); - tokio::spawn(async { run_node(swarm).await }); + let node = prepare_node(key).await; + swarms.push(node.swarm.clone()); + let stabilizer = Arc::new(node.swarm.stabilizer()); + nodes.push(node); + tokio::spawn(stabilizer.wait(Duration::from_secs(3))); } - let swarm1 = Arc::clone(&nodes[0]); - for swarm in nodes.iter().skip(1) { + let swarm1 = Arc::clone(&swarms[0]); + for swarm in swarms.iter().skip(1) { manually_establish_connection(&swarm1, swarm).await; } sleep(Duration::from_secs(9)).await; let mut expected_dhts = vec![]; - for node in nodes.iter() { - let dht = gen_pure_dht(node.did()).await.unwrap(); - for other in nodes.iter() { + for swarm in swarms.iter() { + let dht = gen_pure_dht(swarm.did()); + for other in swarms.iter() { if dht.did != other.did() { dht.join(other.did()).unwrap(); dht.notify(other.did()).unwrap(); @@ -164,18 +137,15 @@ async fn test_stabilization_final_dht() -> Result<()> { } let mut current_dhts = vec![]; - for node in nodes.iter() { + for swarm in swarms.iter() { println!( "Connected peers: {:?}", - SwarmInspect::inspect(node).await.connections + SwarmInspect::inspect(swarm).await.peers ); - current_dhts.push(DHTInspect::inspect(&node.dht())); + current_dhts.push(DHTInspect::inspect(&swarm.dht())); } - for (i, (cur, exp)) in std::iter::zip(current_dhts, expected_dhts) - .enumerate() - .skip(2) - { + for (i, (cur, exp)) in std::iter::zip(current_dhts, expected_dhts).enumerate() { println!("Check node{}", i); pretty_assertions::assert_eq!(cur, exp); } diff --git a/crates/core/src/tests/mod.rs b/crates/core/src/tests/mod.rs index 45915ad72..ad004f3ca 100644 --- a/crates/core/src/tests/mod.rs +++ b/crates/core/src/tests/mod.rs @@ -1,5 +1,3 @@ -use rings_transport::core::transport::ConnectionInterface; - use crate::swarm::Swarm; #[cfg(feature = "wasm")] @@ -18,24 +16,13 @@ pub fn setup_tracing() { } pub async fn manually_establish_connection(swarm1: &Swarm, swarm2: &Swarm) { - assert!(swarm1.get_connection(swarm2.did()).is_none()); - assert!(swarm2.get_connection(swarm1.did()).is_none()); - - let conn1 = swarm1.new_connection(swarm2.did()).await.unwrap(); - let conn2 = swarm2.new_connection(swarm1.did()).await.unwrap(); - - let offer = conn1.webrtc_create_offer().await.unwrap(); - let answer = conn2.webrtc_answer_offer(offer).await.unwrap(); - conn1.webrtc_accept_answer(answer).await.unwrap(); + assert!(swarm1.transport.get_connection(swarm2.did()).is_none()); + assert!(swarm2.transport.get_connection(swarm1.did()).is_none()); - assert!(swarm1.get_connection(swarm2.did()).is_some()); - assert!(swarm2.get_connection(swarm1.did()).is_some()); + let offer = swarm1.create_offer(swarm2.did()).await.unwrap(); + let answer = swarm2.answer_offer(offer).await.unwrap(); + swarm1.accept_answer(answer).await.unwrap(); - // Wait for connection established - swarm1 - .get_connection(swarm2.did()) - .unwrap() - .webrtc_wait_for_data_channel_open() - .await - .unwrap(); + assert!(swarm1.transport.get_connection(swarm2.did()).is_some()); + assert!(swarm2.transport.get_connection(swarm1.did()).is_some()); } diff --git a/crates/core/src/tests/wasm/mod.rs b/crates/core/src/tests/wasm/mod.rs index 7a95ef9a1..757ada437 100644 --- a/crates/core/src/tests/wasm/mod.rs +++ b/crates/core/src/tests/wasm/mod.rs @@ -8,7 +8,6 @@ use crate::storage::idb::IdbStorage; use crate::swarm::Swarm; use crate::swarm::SwarmBuilder; -mod test_channel; mod test_fn_macro; mod test_ice_servers; mod test_idb_storage; diff --git a/crates/core/src/tests/wasm/test_channel.rs b/crates/core/src/tests/wasm/test_channel.rs deleted file mode 100644 index a4a21f71c..000000000 --- a/crates/core/src/tests/wasm/test_channel.rs +++ /dev/null @@ -1,47 +0,0 @@ -use wasm_bindgen_futures::future_to_promise; -use wasm_bindgen_futures::spawn_local; -use wasm_bindgen_futures::JsFuture; -use wasm_bindgen_test::wasm_bindgen_test; - -#[wasm_bindgen_test] -async fn test_std_channel() { - let (sender, receiver) = std::sync::mpsc::sync_channel(1); - async { - sender.send("test").unwrap(); - } - .await; - async { - assert_eq!(receiver.recv().unwrap(), "test"); - } - .await; -} - -// ref https://rustwasm.github.io/wasm-bindgen/api/src/wasm_bindgen_futures/lib.rs.html#NaN -#[wasm_bindgen_test] -async fn test_async_channel_via_future_to_promise() { - let (sender, mut receiver) = futures::channel::mpsc::channel(1); - let fut = async move { - let mut sender = sender.clone(); - assert!(!sender.is_closed()); - sender.try_send("test").unwrap(); - Ok("ok".into()) - }; - let promise = future_to_promise(fut); - JsFuture::from(promise).await.unwrap(); - assert_eq!(receiver.try_next().unwrap(), Some("test")); -} - -// ref https://rustwasm.github.io/wasm-bindgen/api/src/wasm_bindgen_futures/lib.rs.html#77-82 -#[wasm_bindgen_test] -fn test_async_channel_in_spawn_local() { - let (sender, mut receiver) = futures::channel::mpsc::channel(1); - spawn_local(async move { - let mut sender = sender.clone(); - assert!(!sender.is_closed()); - sender.try_send("test").unwrap(); - }); - spawn_local(async move { - let next = receiver.try_next().unwrap(); - assert_eq!(next, Some("test")); - }) -} diff --git a/crates/core/src/tests/wasm/test_wasm_transport.rs b/crates/core/src/tests/wasm/test_wasm_transport.rs index e98721f7a..3647b7f67 100644 --- a/crates/core/src/tests/wasm/test_wasm_transport.rs +++ b/crates/core/src/tests/wasm/test_wasm_transport.rs @@ -1,6 +1,4 @@ -use std::str::FromStr; -use std::sync::Arc; - +use rings_transport::core::callback::TransportCallback; use rings_transport::core::transport::ConnectionInterface; use rings_transport::core::transport::TransportInterface; use rings_transport::core::transport::WebrtcConnectionState; @@ -9,20 +7,12 @@ use wasm_bindgen_futures::JsFuture; use wasm_bindgen_test::*; use super::prepare_node; -use crate::channels::Channel as CbChannel; -use crate::dht::Did; use crate::ecc::SecretKey; -use crate::error::Result; -use crate::swarm::callback::InnerSwarmCallback; -use crate::swarm::callback::SwarmCallback; +use crate::swarm::transport::Transport; use crate::tests::manually_establish_connection; -use crate::types::channel::Channel; -use crate::types::channel::TransportEvent; -use crate::types::Connection; -use crate::types::Transport; struct DefaultCallback; -impl SwarmCallback for DefaultCallback {} +impl TransportCallback for DefaultCallback {} async fn get_fake_permission() { let window = web_sys::window().unwrap(); @@ -36,25 +26,23 @@ async fn get_fake_permission() { JsFuture::from(promise).await.unwrap(); } -async fn prepare_transport(channel: Option>>) -> Transport { - let ch = match channel { - Some(c) => Arc::clone(&c), - None => Arc::new( as Channel>::new()), - }; +async fn prepare_transport() -> Transport { let trans = Transport::new("stun://stun.l.google.com:19302", None); - let callback = InnerSwarmCallback::new( - Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap(), - ch.sender(), - Arc::new(DefaultCallback {}), - ); trans - .new_connection("test", Box::new(callback)) + .new_connection("test", Box::new(DefaultCallback)) .await .unwrap(); trans } -pub async fn establish_ice_connection(conn1: &Connection, conn2: &Connection) -> Result<()> { +#[wasm_bindgen_test] +async fn test_ice_connection_establish() { + get_fake_permission().await; + let trans1 = prepare_transport().await; + let conn1 = trans1.connection("test").unwrap(); + let trans2 = prepare_transport().await; + let conn2 = trans2.connection("test").unwrap(); + assert_eq!(conn1.webrtc_connection_state(), WebrtcConnectionState::New); assert_eq!(conn2.webrtc_connection_state(), WebrtcConnectionState::New); @@ -70,18 +58,6 @@ pub async fn establish_ice_connection(conn1: &Connection, conn2: &Connection) -> WebrtcConnectionState::Connected ); } - - Ok(()) -} - -#[wasm_bindgen_test] -async fn test_ice_connection_establish() { - get_fake_permission().await; - let trans1 = prepare_transport(None).await; - let conn1 = trans1.connection("test").unwrap(); - let trans2 = prepare_transport(None).await; - let conn2 = trans2.connection("test").unwrap(); - establish_ice_connection(&conn1, &conn2).await.unwrap(); } #[wasm_bindgen_test] diff --git a/crates/core/src/types/channel.rs b/crates/core/src/types/channel.rs deleted file mode 100644 index 3694b05ae..000000000 --- a/crates/core/src/types/channel.rs +++ /dev/null @@ -1,27 +0,0 @@ -use async_trait::async_trait; -use serde::Serialize; - -use crate::dht::Did; -use crate::error::Result; - -/// TransportEvent send and recv through Channel. -#[derive(Debug, PartialEq, Eq, Serialize, Clone)] -pub enum TransportEvent { - Connected(Did), - DataChannelMessage(Vec), - Closed(Did), -} - -/// Channel trant implement methods. -#[cfg_attr(feature = "wasm", async_trait(?Send))] -#[cfg_attr(not(feature = "wasm"), async_trait)] -pub trait Channel { - type Sender; - type Receiver; - - fn new() -> Self; - fn sender(&self) -> Self::Sender; - fn receiver(&self) -> Self::Receiver; - async fn send(sender: &Self::Sender, msg: T) -> Result<()>; - async fn recv(receiver: &Self::Receiver) -> Result>; -} diff --git a/crates/core/src/types/mod.rs b/crates/core/src/types/mod.rs deleted file mode 100644 index 8e59123cb..000000000 --- a/crates/core/src/types/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Core traits, including Channel, IceTransport - -/// Channel trait and message. -pub mod channel; - -use rings_transport::connection_ref::ConnectionRef; -#[cfg(feature = "dummy")] -pub use rings_transport::connections::DummyConnection as ConnectionOwner; -#[cfg(feature = "dummy")] -pub use rings_transport::connections::DummyTransport as Transport; -#[cfg(feature = "wasm")] -pub use rings_transport::connections::WebSysWebrtcConnection as ConnectionOwner; -#[cfg(feature = "wasm")] -pub use rings_transport::connections::WebSysWebrtcTransport as Transport; -#[cfg(all(not(feature = "wasm"), not(feature = "dummy")))] -pub use rings_transport::connections::WebrtcConnection as ConnectionOwner; -#[cfg(all(not(feature = "wasm"), not(feature = "dummy")))] -pub use rings_transport::connections::WebrtcTransport as Transport; - -pub type Connection = ConnectionRef; diff --git a/crates/node/bin/rings.rs b/crates/node/bin/rings.rs index 4f3671f3c..4e29822e1 100644 --- a/crates/node/bin/rings.rs +++ b/crates/node/bin/rings.rs @@ -133,10 +133,10 @@ struct RunCommand { #[arg( long, - help = "Stabilize service timeout. If not provided, use stabilize_timeout in config file or 3", + help = "Stabilization interval in seconds. If not provided, use stabilize_interval in config file or 3", env )] - pub stabilize_timeout: Option, + pub stabilize_interval: Option, #[arg(long, help = "external ip address", env)] pub external_ip: Option, @@ -403,8 +403,8 @@ async fn daemon_run(args: RunCommand) -> anyhow::Result<()> { if let Some(external_ip) = args.external_ip { c.external_ip = Some(external_ip); } - if let Some(stabilize_timeout) = args.stabilize_timeout { - c.stabilize_timeout = stabilize_timeout; + if let Some(stabilize_interval) = args.stabilize_interval { + c.stabilize_interval = stabilize_interval; } if let Some(external_api_addr) = args.external_api_addr { c.external_api_addr = external_api_addr; diff --git a/crates/node/src/native/cli.rs b/crates/node/src/native/cli.rs index e58a0a1df..863f4cb9b 100644 --- a/crates/node/src/native/cli.rs +++ b/crates/node/src/native/cli.rs @@ -71,8 +71,6 @@ impl Client { }) .await .map_err(|e| anyhow::anyhow!("{}", e))? - .peer - .ok_or_else(|| anyhow::anyhow!("peer did not return"))? .did; ClientOutput::ok(format!("Remote did: {}", peer_did), peer_did) diff --git a/crates/node/src/native/config.rs b/crates/node/src/native/config.rs index efa67b00f..74fdb84c1 100644 --- a/crates/node/src/native/config.rs +++ b/crates/node/src/native/config.rs @@ -33,7 +33,7 @@ pub const DEFAULT_INTERNAL_API_PORT: u16 = 50000; pub const DEFAULT_EXTERNAL_API_ADDR: &str = "127.0.0.1:50001"; pub const DEFAULT_ENDPOINT_URL: &str = "http://127.0.0.1:50000"; pub const DEFAULT_ICE_SERVERS: &str = "stun://stun.l.google.com:19302"; -pub const DEFAULT_STABILIZE_TIMEOUT: u64 = 3; +pub const DEFAULT_STABILIZE_INTERVAL: u64 = 3; pub const DEFAULT_STORAGE_CAPACITY: u32 = 200000000; pub fn get_storage_location

(prefix: P, path: P) -> String @@ -57,7 +57,7 @@ pub struct Config { pub external_api_addr: String, pub endpoint_url: String, pub ice_servers: String, - pub stabilize_timeout: u64, + pub stabilize_interval: u64, #[serde(skip_serializing_if = "Option::is_none")] pub external_ip: Option, /// When there is no configuration in the YAML file, @@ -98,14 +98,14 @@ impl TryFrom for ProcessorConfigSerialized { Ok(Self::new_with_ext_addr( config.ice_servers, session_sk, - config.stabilize_timeout, + config.stabilize_interval, ext_ip, )) } else { Ok(Self::new( config.ice_servers, session_sk, - config.stabilize_timeout, + config.stabilize_interval, )) } } @@ -139,7 +139,7 @@ impl Config { external_api_addr: DEFAULT_EXTERNAL_API_ADDR.to_string(), endpoint_url: DEFAULT_ENDPOINT_URL.to_string(), ice_servers: DEFAULT_ICE_SERVERS.to_string(), - stabilize_timeout: DEFAULT_STABILIZE_TIMEOUT, + stabilize_interval: DEFAULT_STABILIZE_INTERVAL, external_ip: None, services: vec![], data_storage: DEFAULT_DATA_STORAGE_CONFIG.clone(), @@ -196,7 +196,7 @@ internal_api_port: 50000 external_api_addr: 127.0.0.1:50001 endpoint_url: http://127.0.0.1:50000 ice_servers: stun://stun.l.google.com:19302 -stabilize_timeout: 3 +stabilize_interval: 3 external_ip: null data_storage: path: /Users/foo/.rings/data diff --git a/crates/node/src/prelude.rs b/crates/node/src/prelude.rs index 8429f3ed7..8d6ab9f5d 100644 --- a/crates/node/src/prelude.rs +++ b/crates/node/src/prelude.rs @@ -9,7 +9,6 @@ pub use self::rings_core::ecc::SecretKey; pub use self::rings_core::message::CustomMessage; pub use self::rings_core::message::Message; pub use self::rings_core::message::MessageHandler; -pub use self::rings_core::message::MessageHandlerEvent; pub use self::rings_core::message::MessagePayload; pub use self::rings_core::message::PayloadSender; pub use self::rings_core::prelude::async_trait::async_trait; @@ -26,4 +25,3 @@ pub use self::rings_core::session::SessionSk; pub use self::rings_core::session::SessionSkBuilder; pub use self::rings_core::swarm::Swarm; pub use self::rings_core::swarm::SwarmBuilder; -pub use self::rings_core::types::Connection; diff --git a/crates/node/src/processor.rs b/crates/node/src/processor.rs index e39c5dd5c..5dee923a9 100644 --- a/crates/node/src/processor.rs +++ b/crates/node/src/processor.rs @@ -4,10 +4,19 @@ use std::str::FromStr; use std::sync::Arc; - +use std::time::Duration; + +use rings_core::dht::Did; +use rings_core::dht::VNodeStorage; +use rings_core::measure::MeasureImpl; +use rings_core::message::Encoded; +use rings_core::message::Encoder; +use rings_core::message::Message; +use rings_core::prelude::uuid; use rings_core::storage::MemStorage; +use rings_core::swarm::Swarm; +use rings_core::swarm::SwarmBuilder; use rings_rpc::protos::rings_node::*; -use rings_transport::core::transport::ConnectionInterface; use serde::Deserialize; use serde::Serialize; @@ -16,18 +25,6 @@ use crate::consts::DATA_REDUNDANT; use crate::error::Error; use crate::error::Result; use crate::measure::PeriodicMeasure; -use crate::prelude::rings_core::dht::Did; -use crate::prelude::rings_core::dht::Stabilization; -use crate::prelude::rings_core::dht::TStabilize; -use crate::prelude::rings_core::dht::VNodeStorage; -use crate::prelude::rings_core::message::Encoded; -use crate::prelude::rings_core::message::Encoder; -use crate::prelude::rings_core::message::Message; -use crate::prelude::rings_core::message::PayloadSender; -use crate::prelude::rings_core::prelude::uuid; -use crate::prelude::rings_core::swarm::MeasureImpl; -use crate::prelude::rings_core::swarm::Swarm; -use crate::prelude::rings_core::swarm::SwarmBuilder; use crate::prelude::vnode; use crate::prelude::wasm_export; use crate::prelude::ChordStorageInterface; @@ -45,19 +42,19 @@ pub struct ProcessorConfig { external_address: Option, /// [SessionSk]. session_sk: SessionSk, - /// Stabilization timeout. - stabilize_timeout: u64, + /// Stabilization interval. + stabilize_interval: Duration, } #[wasm_export] impl ProcessorConfig { /// Creates a new `ProcessorConfig` instance without an external address. - pub fn new(ice_servers: String, session_sk: SessionSk, stabilize_timeout: u64) -> Self { + pub fn new(ice_servers: String, session_sk: SessionSk, stabilize_interval: u64) -> Self { Self { ice_servers, external_address: None, session_sk, - stabilize_timeout, + stabilize_interval: Duration::from_secs(stabilize_interval), } } @@ -65,14 +62,14 @@ impl ProcessorConfig { pub fn new_with_ext_addr( ice_servers: String, session_sk: SessionSk, - stabilize_timeout: u64, + stabilize_interval: u64, external_address: String, ) -> Self { Self { ice_servers, external_address: Some(external_address), session_sk, - stabilize_timeout, + stabilize_interval: Duration::from_secs(stabilize_interval), } } @@ -101,18 +98,18 @@ pub struct ProcessorConfigSerialized { external_address: Option, /// A string representing the dumped `SessionSk`. session_sk: String, - /// An unsigned integer representing the stabilization timeout. - stabilize_timeout: u64, + /// An unsigned integer representing the stabilization interval in seconds. + stabilize_interval: u64, } impl ProcessorConfigSerialized { /// Creates a new `ProcessorConfigSerialized` instance without an external address. - pub fn new(ice_servers: String, session_sk: String, stabilize_timeout: u64) -> Self { + pub fn new(ice_servers: String, session_sk: String, stabilize_interval: u64) -> Self { Self { ice_servers, external_address: None, session_sk, - stabilize_timeout, + stabilize_interval, } } @@ -120,14 +117,14 @@ impl ProcessorConfigSerialized { pub fn new_with_ext_addr( ice_servers: String, session_sk: String, - stabilize_timeout: u64, + stabilize_interval: u64, external_address: String, ) -> Self { Self { ice_servers, external_address: Some(external_address), session_sk, - stabilize_timeout, + stabilize_interval, } } } @@ -139,7 +136,7 @@ impl TryFrom for ProcessorConfigSerialized { ice_servers: ins.ice_servers.clone(), external_address: ins.external_address.clone(), session_sk: ins.session_sk.dump()?, - stabilize_timeout: ins.stabilize_timeout, + stabilize_interval: ins.stabilize_interval.as_secs(), }) } } @@ -151,7 +148,7 @@ impl TryFrom for ProcessorConfig { ice_servers: ins.ice_servers.clone(), external_address: ins.external_address.clone(), session_sk: SessionSk::from_str(&ins.session_sk)?, - stabilize_timeout: ins.stabilize_timeout, + stabilize_interval: Duration::from_secs(ins.stabilize_interval), }) } } @@ -191,7 +188,7 @@ pub struct ProcessorBuilder { session_sk: SessionSk, storage: Option, measure: Option, - stabilize_timeout: u64, + stabilize_interval: Duration, } /// Processor for rings-node rpc server @@ -199,8 +196,7 @@ pub struct ProcessorBuilder { pub struct Processor { /// a swarm instance pub swarm: Arc, - /// a stabilization instance, - pub stabilization: Arc, + stabilize_interval: Duration, } impl ProcessorBuilder { @@ -219,7 +215,7 @@ impl ProcessorBuilder { session_sk: config.session_sk.clone(), storage: None, measure: None, - stabilize_timeout: config.stabilize_timeout, + stabilize_interval: config.stabilize_interval, }) } @@ -254,47 +250,33 @@ impl ProcessorBuilder { swarm_builder = swarm_builder.measure(measure); } let swarm = Arc::new(swarm_builder.build()); - let stabilization = Arc::new(Stabilization::new(swarm.clone(), self.stabilize_timeout)); Ok(Processor { swarm, - stabilization, + stabilize_interval: self.stabilize_interval, }) } } -impl Processor { - /// Listen processor message - pub async fn listen(&self) { - let swarm = self.swarm.clone(); - let message_listener = async { swarm.listen().await }; - - let stb = self.stabilization.clone(); - let stabilization = async { stb.wait().await }; - - futures::future::join(message_listener, stabilization).await; - } -} - impl Processor { /// Get current did pub fn did(&self) -> Did { self.swarm.did() } + /// Run stabilization daemon + pub async fn listen(&self) { + let stabilizer = self.swarm.stabilizer(); + Arc::new(stabilizer).wait(self.stabilize_interval).await + } + /// Connect peer with web3 did. /// There are 3 peers: PeerA, PeerB, PeerC. /// 1. PeerA has a connection with PeerB. /// 2. PeerC has a connection with PeerB. /// 3. PeerC can connect PeerA with PeerA's web3 address. - pub async fn connect_with_did(&self, did: Did, wait_for_open: bool) -> Result<()> { - let conn = self.swarm.connect(did).await.map_err(Error::ConnectError)?; - if wait_for_open { - tracing::debug!("wait for connection connected"); - conn.webrtc_wait_for_data_channel_open() - .await - .map_err(|e| Error::ConnectError(rings_core::error::Error::Transport(e)))?; - } + pub async fn connect_with_did(&self, did: Did) -> Result<()> { + self.swarm.connect(did).await.map_err(Error::ConnectError)?; Ok(()) } @@ -306,18 +288,6 @@ impl Processor { .map_err(Error::CloseConnectionError) } - /// Disconnect all connections. - pub async fn disconnect_all(&self) { - let dids = self.swarm.get_connection_ids(); - - let close_async = dids - .into_iter() - .map(|did| self.swarm.disconnect(did)) - .collect::>(); - - futures::future::join_all(close_async).await; - } - /// Send custom message to a did. pub async fn send_message(&self, destination: Did, msg: &[u8]) -> Result { tracing::info!( @@ -404,8 +374,6 @@ impl Processor { mod test { use futures::lock::Mutex; use rings_core::swarm::callback::SwarmCallback; - use rings_core::swarm::impls::ConnectionHandshake; - use rings_transport::core::transport::WebrtcConnectionState; use super::*; use crate::prelude::*; @@ -416,9 +384,9 @@ mod test { let peer_did = SecretKey::random().address().into(); let processor = prepare_processor().await; processor.swarm.create_offer(peer_did).await.unwrap(); - let conn_dids = processor.swarm.get_connection_ids(); + let conn_dids = processor.swarm.peers(); assert_eq!(conn_dids.len(), 1); - assert_eq!(conn_dids.first().unwrap(), &peer_did); + assert_eq!(conn_dids.first().unwrap().did, peer_did.to_string()); } struct SwarmCallbackInstance { @@ -464,53 +432,44 @@ mod test { println!("p1_did: {}", did1); println!("p2_did: {}", did2); - let swarm1 = p1.swarm.clone(); - let swarm2 = p2.swarm.clone(); - tokio::spawn(async { swarm1.listen().await }); - tokio::spawn(async { swarm2.listen().await }); - - let (conn1, offer) = p1.swarm.create_offer(p2.did()).await.unwrap(); + let offer = p1.swarm.create_offer(p2.did()).await.unwrap(); assert_eq!( p1.swarm - .get_connection(p2.did()) + .peers() + .into_iter() + .find(|peer| peer.did == p2.did().to_string()) .unwrap() - .webrtc_connection_state(), - WebrtcConnectionState::New, + .state, + "New" ); - let (conn2, answer) = p2.swarm.answer_offer(offer).await.unwrap(); - let (peer_did, _) = p1.swarm.accept_answer(answer).await.unwrap(); - assert_eq!( - peer_did, did2, - "peer.address got {}, expect: {}", - peer_did, did2 - ); + let answer = p2.swarm.answer_offer(offer).await.unwrap(); + p1.swarm.accept_answer(answer).await.unwrap(); println!("waiting for connection"); - conn1.webrtc_wait_for_data_channel_open().await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; - assert!(conn1.is_connected().await, "conn1 not connected"); - assert!( + assert_eq!( p1.swarm - .get_connection(p2.did()) + .peers() + .into_iter() + .find(|peer| peer.did == p2.did().to_string()) .unwrap() - .is_connected() - .await, + .state, + "Connected", "p1 connection not connected" ); - assert!(conn2.is_connected().await, "conn2 not connected"); - assert!( + assert_eq!( p2.swarm - .get_connection(p1.did()) + .peers() + .into_iter() + .find(|peer| peer.did == p1.did().to_string()) .unwrap() - .is_connected() - .await, + .state, + "Connected", "p2 connection not connected" ); - println!("waiting for data channel ready"); - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - let test_text1 = "test1"; let test_text2 = "test2"; @@ -518,14 +477,11 @@ mod test { let uuid1 = p1.send_message(did2, test_text1.as_bytes()).await.unwrap(); println!("send_message 1 done, msg id: {}", uuid1); - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - println!("send_message 2"); let uuid2 = p2.send_message(did1, test_text2.as_bytes()).await.unwrap(); println!("send_message 2 done, msg id: {}", uuid2); - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - + tokio::time::sleep(std::time::Duration::from_secs(1)).await; println!("check received"); let mut msgs2 = callback2.msgs.try_lock().unwrap(); diff --git a/crates/node/src/provider/browser/provider.rs b/crates/node/src/provider/browser/provider.rs index 239305420..a6e2b21dd 100644 --- a/crates/node/src/provider/browser/provider.rs +++ b/crates/node/src/provider/browser/provider.rs @@ -18,10 +18,6 @@ use rings_core::utils::js_utils; use rings_core::utils::js_value; use rings_derive::wasm_export; use rings_rpc::protos::rings_node::*; -use rings_transport::core::transport::ConnectionInterface; -use rings_transport::core::transport::WebrtcConnectionState; -use serde::Deserialize; -use serde::Serialize; use wasm_bindgen; use wasm_bindgen::prelude::*; use wasm_bindgen_futures; @@ -47,29 +43,6 @@ pub enum AddressType { Ed25519, } -#[derive(Clone, Serialize, Deserialize)] -pub(crate) struct Peer { - pub did: String, - pub state: String, -} - -impl Peer { - fn new(did: Did, state: WebrtcConnectionState) -> Self { - Self { - did: did.to_string(), - state: format!("{:?}", state), - } - } -} - -impl TryFrom<&Peer> for JsValue { - type Error = JsError; - - fn try_from(value: &Peer) -> Result { - js_value::serialize(value).map_err(JsError::from) - } -} - #[wasm_export] impl Provider { /// Create new instance of Provider, return Promise @@ -83,7 +56,7 @@ impl Provider { #[wasm_bindgen(constructor)] pub fn new_instance( ice_servers: String, - stabilize_timeout: u64, + stabilize_interval: u64, account: String, account_type: String, signer: js_sys::Function, @@ -127,7 +100,7 @@ impl Provider { let provider = Provider::new_provider_internal( ice_servers, - stabilize_timeout, + stabilize_interval, account, account_type, Signer::Async(Box::new(signer)), @@ -239,23 +212,7 @@ impl Provider { ) } - /// connect peer with web3 address, without waiting for connection channel connected - pub fn connect_with_address_without_wait( - &self, - address: String, - addr_type: Option, - ) -> js_sys::Promise { - let p = self.processor.clone(); - future_to_promise(async move { - let did = get_did(address.as_str(), addr_type.unwrap_or(AddressType::DEFAULT))?; - p.connect_with_did(did, false) - .await - .map_err(JsError::from)?; - Ok(JsValue::null()) - }) - } - - /// connect peer with web3 address, and wait for connection channel connected + /// connect peer with web3 address /// example: /// ```typescript /// const provider1 = new Provider() @@ -273,28 +230,11 @@ impl Provider { let p = self.processor.clone(); future_to_promise(async move { let did = get_did(address.as_str(), addr_type.unwrap_or(AddressType::DEFAULT))?; - p.connect_with_did(did, true).await.map_err(JsError::from)?; + p.connect_with_did(did).await.map_err(JsError::from)?; Ok(JsValue::null()) }) } - /// list all connect peers - pub fn list_peers(&self) -> js_sys::Promise { - let p = self.processor.clone(); - future_to_promise(async move { - let peers = p - .swarm - .get_connections() - .into_iter() - .flat_map(|(did, conn)| { - JsValue::try_from(&Peer::new(did, conn.webrtc_connection_state())) - }); - - let js_array = js_sys::Array::from_iter(peers); - Ok(js_array.into()) - }) - } - /// get info for self, will return build version and inspection of swarm pub fn get_node_info(&self) -> js_sys::Promise { let p = self.processor.clone(); @@ -316,15 +256,6 @@ impl Provider { }) } - /// disconnect all connected nodes - pub fn disconnect_all(&self) -> js_sys::Promise { - let p = self.processor.clone(); - future_to_promise(async move { - p.disconnect_all().await; - Ok(JsValue::from_str("ok")) - }) - } - /// send custom message to peer. pub fn send_message(&self, destination: String, msg: js_sys::Uint8Array) -> js_sys::Promise { let p = self.processor.clone(); @@ -337,56 +268,6 @@ impl Provider { }) } - /// get peer by address - pub fn get_peer(&self, address: String, addr_type: Option) -> js_sys::Promise { - let p = self.processor.clone(); - future_to_promise(async move { - let did = get_did(address.as_str(), addr_type.unwrap_or(AddressType::DEFAULT))?; - match p.swarm.get_connection(did) { - None => Ok(JsValue::null()), - Some(conn) => { - let peer = Peer::new(did, conn.webrtc_connection_state()); - Ok(JsValue::try_from(&peer)?) - } - } - }) - } - - /// wait for connection connected - /// * address: peer's address - pub fn wait_for_connected( - &self, - address: String, - addr_type: Option, - ) -> js_sys::Promise { - self.wait_for_data_channel_open(address, addr_type) - } - - /// wait for data channel open - /// * address: peer's address - pub fn wait_for_data_channel_open( - &self, - address: String, - addr_type: Option, - ) -> js_sys::Promise { - let p = self.processor.clone(); - future_to_promise(async move { - let did = get_did(address.as_str(), addr_type.unwrap_or(AddressType::DEFAULT))?; - let conn = p - .swarm - .get_connection(did) - .ok_or_else(|| JsError::new("peer not found"))?; - - log::debug!("wait_for_data_channel_open start"); - if let Err(e) = conn.webrtc_wait_for_data_channel_open().await { - log::warn!("wait_for_data_channel_open failed: {}", e); - } - - log::debug!("wait_for_data_channel_open done"); - Ok(JsValue::null()) - }) - } - /// Check local cache pub fn storage_check_cache( &self, diff --git a/crates/node/src/provider/ffi.rs b/crates/node/src/provider/ffi.rs index 98fc857c2..60a0bc283 100644 --- a/crates/node/src/provider/ffi.rs +++ b/crates/node/src/provider/ffi.rs @@ -222,7 +222,7 @@ pub extern "C" fn request( #[no_mangle] pub unsafe extern "C" fn new_provider_with_callback( ice_server: *const c_char, - stabilize_timeout: u64, + stabilize_interval: u64, account: *const c_char, account_type: *const c_char, signer: extern "C" fn(*const c_char, *mut c_char) -> (), @@ -255,7 +255,7 @@ pub unsafe extern "C" fn new_provider_with_callback( executor::block_on(Provider::new_provider_internal( ice, - stabilize_timeout, + stabilize_interval, acc, acc_ty, Signer::Sync(Box::new(wrapped_signer(signer))), diff --git a/crates/node/src/provider/mod.rs b/crates/node/src/provider/mod.rs index 1e329eb8a..c5199d03d 100644 --- a/crates/node/src/provider/mod.rs +++ b/crates/node/src/provider/mod.rs @@ -99,7 +99,7 @@ impl Provider { /// Signer should function as same as account_type declared, Eg: eip191 or secp256k1 or ed25519. pub(crate) async fn new_provider_internal( ice_servers: String, - stabilize_timeout: u64, + stabilize_interval: u64, account: String, account_type: String, signer: Signer, @@ -114,7 +114,7 @@ impl Provider { }; sk_builder = sk_builder.set_session_sig(sig.to_vec()); let session_sk = sk_builder.build().map_err(Error::InternalError)?; - let config = ProcessorConfig::new(ice_servers, session_sk, stabilize_timeout); + let config = ProcessorConfig::new(ice_servers, session_sk, stabilize_interval); Self::new_provider_with_storage_internal(config, vnode_storage, measure_storage).await } diff --git a/crates/node/src/rpc_impl.rs b/crates/node/src/rpc_impl.rs index f5743fd7f..64c2500d7 100644 --- a/crates/node/src/rpc_impl.rs +++ b/crates/node/src/rpc_impl.rs @@ -18,11 +18,10 @@ use rings_core::message::Decoder; use rings_core::message::Encoded; use rings_core::message::Encoder; use rings_core::message::MessagePayload; +use rings_core::message::MessageVerificationExt; use rings_core::prelude::vnode::VirtualNode; -use rings_core::swarm::impls::ConnectionHandshake; use rings_rpc::protos::rings_node::*; use rings_rpc::protos::rings_node_handler::HandleRpc; -use rings_transport::core::transport::ConnectionInterface; use crate::error::Error as ServerError; use crate::processor::Processor; @@ -43,7 +42,10 @@ impl HandleRpc for Proces .map_err(|e| ServerError::RemoteRpcError(e.to_string()))? .did; - let offer = self.handle_rpc(CreateOfferRequest { did }).await?.offer; + let offer = self + .handle_rpc(CreateOfferRequest { did: did.clone() }) + .await? + .offer; let answer = client .answer_offer(&AnswerOfferRequest { offer }) @@ -51,9 +53,9 @@ impl HandleRpc for Proces .map_err(|e| ServerError::RemoteRpcError(e.to_string()))? .answer; - let peer = self.handle_rpc(AcceptAnswerRequest { answer }).await?.peer; + self.handle_rpc(AcceptAnswerRequest { answer }).await?; - Ok(ConnectPeerViaHttpResponse { peer }) + Ok(ConnectPeerViaHttpResponse { did }) } } @@ -62,10 +64,7 @@ impl HandleRpc for Proces impl HandleRpc for Processor { async fn handle_rpc(&self, req: ConnectWithDidRequest) -> Result { let did = s2d(&req.did)?; - self.connect_with_did(did, true) - .await - .map_err(Error::from)?; - + self.connect_with_did(did).await.map_err(Error::from)?; Ok(ConnectWithDidResponse {}) } } @@ -76,8 +75,9 @@ impl HandleRpc for Processor { async fn handle_rpc(&self, req: ConnectWithSeedRequest) -> Result { let seed: Seed = Seed::try_from(req)?; - let mut connected: HashSet = HashSet::from_iter(self.swarm.get_connection_ids()); - connected.insert(self.swarm.did()); + let mut connected: HashSet = + HashSet::from_iter(self.swarm.peers().into_iter().map(|peer| peer.did)); + connected.insert(self.swarm.did().to_string()); let tasks = seed .peers @@ -104,7 +104,12 @@ impl HandleRpc for Processor { #[cfg_attr(not(feature = "browser"), async_trait)] impl HandleRpc for Processor { async fn handle_rpc(&self, _req: ListPeersRequest) -> Result { - let peers = self.swarm.get_connections().into_iter().map(dc2p).collect(); + let peers = self + .swarm + .peers() + .into_iter() + .map(|peer| peer.into()) + .collect(); Ok(ListPeersResponse { peers }) } } @@ -114,7 +119,7 @@ impl HandleRpc for Processor { impl HandleRpc for Processor { async fn handle_rpc(&self, req: CreateOfferRequest) -> Result { let did = s2d(&req.did)?; - let (_, offer_payload) = self + let offer_payload = self .swarm .create_offer(did) .await @@ -143,7 +148,7 @@ impl HandleRpc for Processor { let offer_payload = MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; - let (_, answer_payload) = self + let answer_payload = self .swarm .answer_offer(offer_payload) .await @@ -172,16 +177,14 @@ impl HandleRpc for Processor { let answer_payload = MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; + answer_payload.transaction.signer(); - let dc = self - .swarm + self.swarm .accept_answer(answer_payload) .await .map_err(ServerError::AcceptAnswer)?; - Ok(AcceptAnswerResponse { - peer: Some(dc2p(dc)), - }) + Ok(AcceptAnswerResponse {}) } } @@ -322,14 +325,6 @@ impl HandleRpc for Processor { } } -/// Convert did and connection to Peer -fn dc2p((did, conn): (Did, impl ConnectionInterface)) -> PeerInfo { - PeerInfo { - did: did.to_string(), - state: format!("{:?}", conn.webrtc_connection_state()), - } -} - /// Get did from string or return InvalidParam Error fn s2d(s: &str) -> Result { Did::from_str(s).map_err(|_| Error::invalid_params(format!("Invalid Did: {s}"))) diff --git a/crates/node/src/seed.rs b/crates/node/src/seed.rs index e16cc0795..fbf74ce4a 100644 --- a/crates/node/src/seed.rs +++ b/crates/node/src/seed.rs @@ -1,7 +1,5 @@ //! Seed and SeedLoader use for getting peers from endpoint. -use std::str::FromStr; -use rings_core::dht::Did; use rings_rpc::protos::rings_node::ConnectWithSeedRequest; use serde::Deserialize; use serde::Serialize; @@ -18,7 +16,7 @@ pub struct Seed { #[derive(Deserialize, Serialize, Debug)] pub struct SeedPeer { /// an unique identify. - pub did: Did, + pub did: String, /// remote client endpoint pub url: String, } @@ -30,8 +28,10 @@ impl TryFrom for Seed { let mut peers = Vec::new(); for peer in req.peers { - let did = Did::from_str(&peer.did).map_err(|_| Error::InvalidDid(peer.did.clone()))?; - peers.push(SeedPeer { did, url: peer.url }); + peers.push(SeedPeer { + did: peer.did, + url: peer.url, + }); } Ok(Seed { peers }) @@ -44,7 +44,7 @@ impl Seed { for peer in self.peers { peers.push(rings_rpc::protos::rings_node::SeedPeer { - did: peer.did.to_string(), + did: peer.did, url: peer.url, }); } diff --git a/crates/node/src/tests/native/mod.rs b/crates/node/src/tests/native/mod.rs index 2fd2394a5..82b1ccc68 100644 --- a/crates/node/src/tests/native/mod.rs +++ b/crates/node/src/tests/native/mod.rs @@ -14,7 +14,7 @@ pub async fn prepare_processor() -> Processor { let config = serde_yaml::to_string(&ProcessorConfig::new( "stun://stun.l.google.com:19302".to_string(), sm, - 200, + 3, )) .unwrap(); diff --git a/crates/node/src/tests/wasm/browser.rs b/crates/node/src/tests/wasm/browser.rs index 8ad3055fd..e93ed733a 100644 --- a/crates/node/src/tests/wasm/browser.rs +++ b/crates/node/src/tests/wasm/browser.rs @@ -9,7 +9,6 @@ use crate::backend::types::BackendMessage; use crate::prelude::rings_core::utils; use crate::prelude::rings_core::utils::js_value; use crate::provider::browser; -use crate::provider::browser::Peer; #[cfg(feature = "browser_chrome_test")] wasm_bindgen_test_configure!(run_in_browser); @@ -34,17 +33,10 @@ async fn test_two_provider_connect_and_list() { assert!(peers.len() == 1, "peers len should be 1"); let peer2 = peers.first().unwrap(); - console_log!("get peer"); - let peer2: Peer = js_value::deserialize( - &JsFuture::from(provider1.get_peer(peer2.did.clone(), None)) - .await - .unwrap(), - ) - .unwrap(); - assert!( - peer2.state.eq("Connected"), + assert_eq!( + peer2.state, "Connected", "peer2 state got {:?}", - peer2.state, + peer2.state ); JsFuture::from(provider1.disconnect(peer2.did.clone(), None)) diff --git a/crates/node/src/tests/wasm/mod.rs b/crates/node/src/tests/wasm/mod.rs index 6df554fb1..755c849ef 100644 --- a/crates/node/src/tests/wasm/mod.rs +++ b/crates/node/src/tests/wasm/mod.rs @@ -7,11 +7,7 @@ use rings_core::ecc::SecretKey; use rings_core::prelude::uuid; use rings_core::session::SessionSk; use rings_core::storage::idb::IdbStorage; -use rings_rpc::protos::rings_node::AcceptAnswerRequest; -use rings_rpc::protos::rings_node::AnswerOfferRequest; -use rings_rpc::protos::rings_node::AnswerOfferResponse; -use rings_rpc::protos::rings_node::CreateOfferRequest; -use rings_rpc::protos::rings_node::CreateOfferResponse; +use rings_rpc::protos::rings_node::*; use wasm_bindgen_futures::JsFuture; use crate::logging::browser::init_logging; @@ -19,7 +15,6 @@ use crate::prelude::rings_core::utils::js_value; use crate::processor::Processor; use crate::processor::ProcessorBuilder; use crate::processor::ProcessorConfig; -use crate::provider::browser::Peer; use crate::provider::Provider; pub fn setup_log() { @@ -57,14 +52,17 @@ pub async fn new_provider() -> Provider { Provider::from_processor(Arc::new(processor)) } -pub async fn get_peers(provider: &Provider) -> Vec { - let peers = JsFuture::from(provider.list_peers()).await.ok().unwrap(); - let peers: js_sys::Array = peers.into(); - let peers: Vec = peers - .iter() - .flat_map(|x| js_value::deserialize(&x).ok()) - .collect::>(); - peers +pub async fn get_peers(provider: &Provider) -> Vec { + let resp = JsFuture::from(provider.request( + "listPeers".to_string(), + js_value::serialize(&ListPeersRequest {}).unwrap(), + )) + .await + .unwrap(); + + js_value::deserialize::(resp) + .unwrap() + .peers } pub async fn create_connection(provider1: &Provider, provider2: &Provider) { diff --git a/crates/node/src/tests/wasm/processor.rs b/crates/node/src/tests/wasm/processor.rs index cf100b1bc..f2dfb1e26 100644 --- a/crates/node/src/tests/wasm/processor.rs +++ b/crates/node/src/tests/wasm/processor.rs @@ -1,36 +1,23 @@ use std::sync::Arc; use std::time::Duration; +use async_trait::async_trait; use futures::lock::Mutex; use rings_core::swarm::callback::SwarmCallback; -use rings_core::swarm::impls::ConnectionHandshake; -use rings_transport::core::transport::ConnectionInterface; -use rings_transport::core::transport::WebrtcConnectionState; use wasm_bindgen_test::*; -use crate::prelude::rings_core::async_trait; -use crate::prelude::rings_core::dht::TStabilize; -use crate::prelude::rings_core::utils; use crate::prelude::*; use crate::processor::*; use crate::tests::wasm::prepare_processor; -async fn listen(p: &Processor) { - let h = p.swarm.clone(); - let s = Arc::clone(&p.stabilization); - - futures::join!( - async { - h.listen().await; - }, - async { - s.wait().await; - } - ); -} - async fn close_all_connections(p: &Processor) { - futures::future::join_all(p.swarm.get_connections().iter().map(|(_, t)| t.close())).await; + futures::future::join_all( + p.swarm + .peers() + .iter() + .map(|peer| p.swarm.disconnect(peer.did.parse().unwrap())), + ) + .await; } struct SwarmCallbackStruct { @@ -53,47 +40,13 @@ impl SwarmCallback for SwarmCallbackStruct { async fn create_connection(p1: &Processor, p2: &Processor) { console_log!("create_offer"); - let (conn_1, offer) = p1.swarm.create_offer(p2.did()).await.unwrap(); + let offer = p1.swarm.create_offer(p2.did()).await.unwrap(); console_log!("answer_offer"); - let (conn_2, answer) = p2.swarm.answer_offer(offer).await.unwrap(); + let answer = p2.swarm.answer_offer(offer).await.unwrap(); console_log!("accept_answer"); p1.swarm.accept_answer(answer).await.unwrap(); - - loop { - console_log!("waiting for connection"); - utils::js_utils::window_sleep(1000).await.unwrap(); - - console_log!("conn_1 state: {:?}", conn_1.webrtc_connection_state()); - console_log!("conn_2 state: {:?}", conn_2.webrtc_connection_state()); - - let s1 = conn_1.get_stats().await; - let s2 = conn_2.get_stats().await; - - console_log!("conn_1 stats: {:?}", s1); - console_log!("conn_2 stats: {:?}", s2); - - if conn_1.is_connected().await && conn_2.is_connected().await { - break; - } - } - - futures::try_join!( - async { - if conn_1.is_connected().await { - return Ok(()); - } - conn_1.webrtc_wait_for_data_channel_open().await - }, - async { - if conn_2.is_connected().await { - return Ok(()); - } - conn_2.webrtc_wait_for_data_channel_open().await - } - ) - .unwrap(); } #[wasm_bindgen_test] @@ -123,8 +76,8 @@ async fn test_processor_handshake_and_msg() { console_log!("p2_did: {}", p2_did); console_log!("listen"); - listen(&p1).await; - listen(&p2).await; + p1.listen().await; + p2.listen().await; console_log!("processor_hs_connect_1_2"); create_connection(&p1, &p2).await; @@ -196,10 +149,6 @@ async fn test_processor_connect_with_did() { let p3 = prepare_processor().await; console_log!("p3 address: {}", p3.did()); - p1.swarm.clone().listen().await; - p2.swarm.clone().listen().await; - p3.swarm.clone().listen().await; - console_log!("processor_connect_p1_and_p2"); create_connection(&p1, &p2).await; console_log!("processor_connect_p1_and_p2, done"); @@ -208,11 +157,9 @@ async fn test_processor_connect_with_did() { create_connection(&p2, &p3).await; console_log!("processor_connect_p2_and_p3, done"); - let p1_peer_dids = p1.swarm.get_connection_ids(); + let peers = p1.swarm.peers(); assert!( - p1_peer_dids - .iter() - .any(|did| did.to_string().eq(&p2.did().to_string())), + peers.iter().any(|peer| peer.did.eq(&p2.did().to_string())), "p2 not in p1's peer list" ); @@ -222,24 +169,25 @@ async fn test_processor_connect_with_did() { console_log!("connect p1 and p3"); // p1 create connect with p3's address - p1.connect_with_did(p3.did(), true).await.unwrap(); - let conn3 = p1.swarm.get_connection(p3.did()).unwrap(); - console_log!("processor_p1_p3_conntected"); + p1.connect_with_did(p3.did()).await.unwrap(); fluvio_wasm_timer::Delay::new(Duration::from_millis(1000)) .await .unwrap(); console_log!("processor_detect_connection_state"); - assert_eq!( - conn3.webrtc_connection_state(), - WebrtcConnectionState::Connected, - ); + let peer3 = p1 + .swarm + .peers() + .into_iter() + .find(|peer| peer.did == p3.did().to_string()) + .unwrap(); + assert_eq!(peer3.state, "Connected"); console_log!("check peers"); - let peer_dids = p1.swarm.get_connection_ids(); + let peers = p1.swarm.peers(); assert!( - peer_dids + peers .iter() - .any(|did| did.to_string().eq(p3.did().to_string().as_str())), + .any(|peer| peer.did.eq(p3.did().to_string().as_str())), "peer list dose NOT contains p3 address" ); console_log!("processor_close_all_connections"); diff --git a/crates/rpc/src/protos/mod.rs b/crates/rpc/src/protos/mod.rs index 124065e07..8877f18e8 100644 --- a/crates/rpc/src/protos/mod.rs +++ b/crates/rpc/src/protos/mod.rs @@ -1,19 +1,13 @@ pub mod rings_node; pub mod rings_node_handler; +use rings_core::inspect::ConnectionInspect; use rings_core::inspect::StorageInspect; use rings_core::inspect::SwarmInspect; impl From for rings_node::SwarmInfo { fn from(inspect: SwarmInspect) -> Self { - let peers = inspect - .connections - .into_iter() - .map(|conn| rings_node::PeerInfo { - did: conn.did, - state: conn.state, - }) - .collect(); + let peers = inspect.peers.into_iter().map(|conn| conn.into()).collect(); let dht = rings_node::DhtInfo { did: inspect.dht.did, @@ -54,3 +48,12 @@ impl From for rings_node::StorageInfo { } } } + +impl From for rings_node::PeerInfo { + fn from(value: ConnectionInspect) -> Self { + rings_node::PeerInfo { + did: value.did, + state: value.state, + } + } +} diff --git a/crates/rpc/src/protos/rings_node.proto b/crates/rpc/src/protos/rings_node.proto index 6c87796a9..ce06c20cd 100644 --- a/crates/rpc/src/protos/rings_node.proto +++ b/crates/rpc/src/protos/rings_node.proto @@ -11,7 +11,7 @@ message ConnectPeerViaHttpRequest { } message ConnectPeerViaHttpResponse { - PeerInfo peer = 1; + string did = 1; } message ConnectWithDidRequest { @@ -57,9 +57,7 @@ message AcceptAnswerRequest { string answer = 1; } -message AcceptAnswerResponse { - PeerInfo peer = 1; -} +message AcceptAnswerResponse {} message DisconnectRequest { string did = 1; diff --git a/crates/rpc/src/protos/rings_node.rs b/crates/rpc/src/protos/rings_node.rs index 33ffa6fb4..f02999fbb 100644 --- a/crates/rpc/src/protos/rings_node.rs +++ b/crates/rpc/src/protos/rings_node.rs @@ -18,8 +18,8 @@ pub struct ConnectPeerViaHttpRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ConnectPeerViaHttpResponse { - #[prost(message, optional, tag = "1")] - pub peer: ::core::option::Option, + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, } #[derive(serde::Serialize, serde::Deserialize)] #[allow(clippy::derive_partial_eq_without_eq)] @@ -101,10 +101,7 @@ pub struct AcceptAnswerRequest { #[derive(serde::Serialize, serde::Deserialize)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct AcceptAnswerResponse { - #[prost(message, optional, tag = "1")] - pub peer: ::core::option::Option, -} +pub struct AcceptAnswerResponse {} #[derive(serde::Serialize, serde::Deserialize)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/transport/Cargo.toml b/crates/transport/Cargo.toml index 4fe47ad8b..361de7546 100644 --- a/crates/transport/Cargo.toml +++ b/crates/transport/Cargo.toml @@ -11,7 +11,7 @@ repository.workspace = true [features] # Include nothing by default -default = ["tokio/time"] +default = ["tokio/time", "tokio-util"] dummy = ["webrtc", "rand", "lazy_static"] native-webrtc = ["webrtc"] web-sys-webrtc = ["wasm-bindgen", "js-sys", "web-sys", "wasm-bindgen-futures"] @@ -19,6 +19,7 @@ web-sys-webrtc = ["wasm-bindgen", "js-sys", "web-sys", "wasm-bindgen-futures"] [dependencies] # Dependencies for native-webrtc feature tokio = { version = "1.32.0", optional = true } +tokio-util = { version = "0.7.8", optional = true } webrtc = { workspace = true, optional = true } # Dependencies for dummy feature diff --git a/crates/transport/src/callback.rs b/crates/transport/src/callback.rs index 9888b7b8e..acc1d1ff9 100644 --- a/crates/transport/src/callback.rs +++ b/crates/transport/src/callback.rs @@ -30,8 +30,11 @@ impl InnerTransportCallback { } /// Notify the data channel is open. - pub fn on_data_channel_open(&self) { - self.data_channel_state_notifier.wake() + pub async fn on_data_channel_open(&self) { + self.data_channel_state_notifier.wake(); + if let Err(e) = self.callback.on_data_channel_open(&self.cid).await { + tracing::error!("Callback on_data_channel_open failed: {e:?}"); + } } /// Notify the data channel is close. diff --git a/crates/transport/src/connections/dummy/mod.rs b/crates/transport/src/connections/dummy/mod.rs index e843a5f5e..a6d7f5d6f 100644 --- a/crates/transport/src/connections/dummy/mod.rs +++ b/crates/transport/src/connections/dummy/mod.rs @@ -22,9 +22,9 @@ use crate::notifier::Notifier; use crate::pool::Pool; /// Max delay in ms on sending message -const DUMMY_DELAY_MAX: u64 = 100; +const DUMMY_DELAY_MAX: u64 = 1000; /// Min delay in ms on sending message -const DUMMY_DELAY_MIN: u64 = 0; +const DUMMY_DELAY_MIN: u64 = 100; /// Config random delay when send message const SEND_MESSAGE_DELAY: bool = true; /// Config random delay when channel opening @@ -93,6 +93,18 @@ impl DummyConnection { } self.callback().on_peer_connection_state_change(state).await; + + // Simulate the behavior where the data channel is not opened immediately upon connection, + // but rather after a certain number of milliseconds. + if state == WebrtcConnectionState::Connected { + let cb = self.callback(); + tokio::spawn(async move { + if CHANNEL_OPEN_DELAY { + random_delay().await; + } + cb.on_data_channel_open().await; + }); + } } } @@ -135,16 +147,18 @@ impl ConnectionInterface for DummyConnection { } async fn webrtc_answer_offer(&self, offer: Self::Sdp) -> Result { + // Set remote rand id before setting state so that the remote connection can be found in callback. + self.set_remote_rand_id(offer); self.set_webrtc_connection_state(WebrtcConnectionState::Connecting) .await; - self.set_remote_rand_id(offer); Ok(self.rand_id.clone()) } async fn webrtc_accept_answer(&self, answer: Self::Sdp) -> Result<()> { + // Set remote rand id before setting state so that the remote connection can be found in callback. + self.set_remote_rand_id(answer); self.set_webrtc_connection_state(WebrtcConnectionState::Connected) .await; - self.set_remote_rand_id(answer); if let Some(remote_conn) = self.remote_conn() { remote_conn diff --git a/crates/transport/src/connections/native_webrtc/mod.rs b/crates/transport/src/connections/native_webrtc/mod.rs index 73a0b5360..e42be3bd5 100644 --- a/crates/transport/src/connections/native_webrtc/mod.rs +++ b/crates/transport/src/connections/native_webrtc/mod.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use async_trait::async_trait; use bytes::Bytes; +use tokio_util::sync::CancellationToken; use webrtc::data_channel::data_channel_message::DataChannelMessage; use webrtc::data_channel::data_channel_state::RTCDataChannelState; use webrtc::data_channel::RTCDataChannel; @@ -29,6 +30,7 @@ use crate::notifier::Notifier; use crate::pool::Pool; const WEBRTC_WAIT_FOR_DATA_CHANNEL_OPEN_TIMEOUT: u8 = 8; // seconds +const WEBRTC_GATHER_TIMEOUT: u8 = 60; // seconds /// A connection that implemented by webrtc-rs library. /// Used for native environment. @@ -36,6 +38,7 @@ pub struct WebrtcConnection { webrtc_conn: RTCPeerConnection, webrtc_data_channel: Arc, webrtc_data_channel_state_notifier: Notifier, + cancel_token: CancellationToken, } /// [WebrtcTransport] manages all the [WebrtcConnection] and @@ -56,15 +59,23 @@ impl WebrtcConnection { webrtc_conn, webrtc_data_channel, webrtc_data_channel_state_notifier, + cancel_token: CancellationToken::new(), } } async fn webrtc_gather(&self) -> Result { - self.webrtc_conn - .gathering_complete_promise() - .await - .recv() - .await; + let mut gathering_complete_promise = self.webrtc_conn.gathering_complete_promise().await; + let gathering_complete_promise_with_timeout = tokio::time::timeout( + std::time::Duration::from_secs(WEBRTC_GATHER_TIMEOUT.into()), + gathering_complete_promise.recv(), + ); + + tokio::select! { + _ = self.cancel_token.cancelled() => { + return Err(Error::WebrtcLocalSdpGenerationError("Local connection closed".to_string())) + } + _ = gathering_complete_promise_with_timeout => {} + } Ok(self .webrtc_conn @@ -168,6 +179,8 @@ impl ConnectionInterface for WebrtcConnection { .set_timeout(WEBRTC_WAIT_FOR_DATA_CHANNEL_OPEN_TIMEOUT); self.webrtc_data_channel_state_notifier.clone().await; + dbg!(self.webrtc_data_channel.ready_state()); + if self.webrtc_data_channel.ready_state() == RTCDataChannelState::Open { return Ok(()); } else { @@ -178,6 +191,7 @@ impl ConnectionInterface for WebrtcConnection { } async fn close(&self) -> Result<()> { + self.cancel_token.cancel(); self.webrtc_conn.close().await.map_err(|e| e.into()) } } @@ -245,8 +259,7 @@ impl TransportInterface for WebrtcTransport { let on_open_inner_cb = data_channel_inner_cb.clone(); d.on_open(Box::new(move || { - on_open_inner_cb.on_data_channel_open(); - Box::pin(async move {}) + Box::pin(async move { on_open_inner_cb.on_data_channel_open().await }) })); let on_close_inner_cb = data_channel_inner_cb.clone(); diff --git a/crates/transport/src/connections/web_sys_webrtc/mod.rs b/crates/transport/src/connections/web_sys_webrtc/mod.rs index 8aeca7cb4..762cd29d4 100644 --- a/crates/transport/src/connections/web_sys_webrtc/mod.rs +++ b/crates/transport/src/connections/web_sys_webrtc/mod.rs @@ -277,7 +277,10 @@ impl TransportInterface for WebSysWebrtcTransport { let on_open_inner_cb = data_channel_inner_cb.clone(); let on_open = Box::new(move || { - on_open_inner_cb.on_data_channel_open(); + let inner_cb = on_open_inner_cb.clone(); + spawn_local(async move { + inner_cb.on_data_channel_open().await; + }) }); let on_close_inner_cb = data_channel_inner_cb.clone(); diff --git a/crates/transport/src/core/callback.rs b/crates/transport/src/core/callback.rs index 076ed42ea..961d1d4fd 100644 --- a/crates/transport/src/core/callback.rs +++ b/crates/transport/src/core/callback.rs @@ -15,6 +15,11 @@ type CallbackError = Box; #[cfg_attr(feature = "web-sys-webrtc", async_trait(?Send))] #[cfg_attr(not(feature = "web-sys-webrtc"), async_trait)] pub trait TransportCallback { + /// Notify the data channel is open. + async fn on_data_channel_open(&self, _cid: &str) -> Result<(), CallbackError> { + Ok(()) + } + /// This method is invoked on a binary message arrival over the data channel of webrtc. async fn on_message(&self, _cid: &str, _msg: &[u8]) -> Result<(), CallbackError> { Ok(()) diff --git a/crates/transport/src/core/transport.rs b/crates/transport/src/core/transport.rs index e627dd279..c5181ff7a 100644 --- a/crates/transport/src/core/transport.rs +++ b/crates/transport/src/core/transport.rs @@ -91,26 +91,6 @@ pub trait ConnectionInterface { /// Close the webrtc connection. async fn close(&self) -> Result<(), Self::Error>; - - /// Deprecated, should use `webrtc_connection_state`. - fn ice_connection_state(&self) -> WebrtcConnectionState { - self.webrtc_connection_state() - } - - /// Deprecated, should check the state of `webrtc_connection_state`. - async fn is_connected(&self) -> bool { - self.webrtc_connection_state() == WebrtcConnectionState::Connected - } - - /// Deprecated, should check the state of `webrtc_connection_state`. - async fn is_disconnected(&self) -> bool { - matches!( - self.webrtc_connection_state(), - WebrtcConnectionState::Disconnected - | WebrtcConnectionState::Failed - | WebrtcConnectionState::Closed - ) - } } /// This trait specifies how to management [ConnectionInterface] objects. diff --git a/examples/ffi/rings.h b/examples/ffi/rings.h index 07a1a4bd5..e2327fc34 100644 --- a/examples/ffi/rings.h +++ b/examples/ffi/rings.h @@ -92,7 +92,7 @@ const char *request(const struct ProviderPtr *provider_ptr, const char *method, * * This function cast CStr into Str */ struct ProviderPtr new_provider_with_callback(const char *ice_server, - uint64_t stabilize_timeout, + uint64_t stabilize_interval, const char *account, const char *account_type, void (*signer)(const char*, char*), diff --git a/examples/native/src/main.rs b/examples/native/src/main.rs index de2d32919..df66993c1 100644 --- a/examples/native/src/main.rs +++ b/examples/native/src/main.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::time::Duration; use async_trait::async_trait; use rings_core::dht::Did; @@ -89,11 +90,11 @@ async fn main() { .unwrap(); println!("<=== ConnectPeerViaHttpResponse: {:?}", resp); - let remote_did = resp.peer.unwrap().did; + let remote_did = resp.did; let connected = 'connected: { for _ in 0..10 { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_secs(1)).await; println!("===> request ListPeers api..."); let resp: ListPeersResponse = serde_json::from_value( @@ -132,5 +133,5 @@ async fn main() { println!("<=== SendBackendMessage: {:?}", resp); // Wait for message sent. - tokio::time::sleep(std::time::Duration::from_secs(3)).await; + tokio::time::sleep(Duration::from_secs(3)).await; }