@ -1,12 +1,33 @@
use crate ::asb ::event_loop ::LatestRate ;
use crate ::env ;
use crate ::network ::quote ::BidQuote ;
use crate ::network ::rendezvous ::XmrBtcNamespace ;
use crate ::network ::swap_setup ::alice ;
use crate ::network ::swap_setup ::alice ::WalletSnapshot ;
use crate ::network ::transport ::authenticate_and_multiplex ;
use crate ::network ::{ encrypted_signature , quote , transfer_proof } ;
use crate ::protocol ::alice ::State3 ;
use anyhow ::{ anyhow , Error , Result } ;
use futures ::FutureExt ;
use libp2p ::core ::connection ::ConnectionId ;
use libp2p ::core ::muxing ::StreamMuxerBox ;
use libp2p ::core ::transport ::Boxed ;
use libp2p ::dns ::TokioDnsConfig ;
use libp2p ::ping ::{ Ping , PingEvent } ;
use libp2p ::request_response ::{ RequestId , ResponseChannel } ;
use libp2p ::swarm ::{
DialPeerCondition , IntoProtocolsHandler , NetworkBehaviour , NetworkBehaviourAction ,
PollParameters , ProtocolsHandler ,
} ;
use libp2p ::tcp ::TokioTcpConfig ;
use libp2p ::websocket ::WsConfig ;
use libp2p ::{ identity , Multiaddr , NetworkBehaviour , PeerId , Transport } ;
use std ::task ::Poll ;
use std ::time ::Duration ;
use uuid ::Uuid ;
pub mod transport {
use crate ::network ::transport ::authenticate_and_multiplex ;
use anyhow ::Result ;
use libp2p ::core ::muxing ::StreamMuxerBox ;
use libp2p ::core ::transport ::Boxed ;
use libp2p ::dns ::TokioDnsConfig ;
use libp2p ::tcp ::TokioTcpConfig ;
use libp2p ::websocket ::WsConfig ;
use libp2p ::{ identity , PeerId , Transport } ;
use super ::* ;
/// Creates the libp2p transport for the ASB.
pub fn new ( identity : & identity ::Keypair ) -> Result < Boxed < ( PeerId , StreamMuxerBox ) > > {
@ -21,18 +42,7 @@ pub mod transport {
}
pub mod behaviour {
use crate ::asb ::event_loop ::LatestRate ;
use crate ::env ;
use crate ::network ::quote ::BidQuote ;
use crate ::network ::swap_setup ::alice ;
use crate ::network ::swap_setup ::alice ::WalletSnapshot ;
use crate ::network ::{ encrypted_signature , quote , transfer_proof } ;
use crate ::protocol ::alice ::State3 ;
use anyhow ::{ anyhow , Error } ;
use libp2p ::ping ::{ Ping , PingEvent } ;
use libp2p ::request_response ::{ RequestId , ResponseChannel } ;
use libp2p ::{ NetworkBehaviour , PeerId } ;
use uuid ::Uuid ;
use super ::* ;
#[ allow(clippy::large_enum_variant) ]
#[ derive(Debug) ]
@ -62,6 +72,7 @@ pub mod behaviour {
channel : ResponseChannel < ( ) > ,
peer : PeerId ,
} ,
Rendezvous ( libp2p ::rendezvous ::Event ) ,
Failure {
peer : PeerId ,
error : Error ,
@ -95,6 +106,7 @@ pub mod behaviour {
where
LR : LatestRate + Send + ' static ,
{
pub rendezvous : libp2p ::swarm ::toggle ::Toggle < rendezous ::Behaviour > ,
pub quote : quote ::Behaviour ,
pub swap_setup : alice ::Behaviour < LR > ,
pub transfer_proof : transfer_proof ::Behaviour ,
@ -116,8 +128,20 @@ pub mod behaviour {
latest_rate : LR ,
resume_only : bool ,
env_config : env ::Config ,
rendezvous_params : Option < ( identity ::Keypair , PeerId , Multiaddr , XmrBtcNamespace ) > ,
) -> Self {
Self {
rendezvous : libp2p ::swarm ::toggle ::Toggle ::from ( rendezvous_params . map (
| ( identity , rendezvous_peer_id , rendezvous_address , namespace ) | {
rendezous ::Behaviour ::new (
identity ,
rendezvous_peer_id ,
rendezvous_address ,
namespace ,
None , // use default ttl on rendezvous point
)
} ,
) ) ,
quote : quote ::asb ( ) ,
swap_setup : alice ::Behaviour ::new (
min_buy ,
@ -138,4 +162,280 @@ pub mod behaviour {
OutEvent ::Other
}
}
impl From < libp2p ::rendezvous ::Event > for OutEvent {
fn from ( event : libp2p ::rendezvous ::Event ) -> Self {
OutEvent ::Rendezvous ( event )
}
}
}
mod rendezous {
use super ::* ;
use std ::pin ::Pin ;
#[ derive(PartialEq) ]
enum ConnectionStatus {
Disconnected ,
Dialling ,
Connected ,
}
enum RegistrationStatus {
RegisterOnNextConnection ,
Pending ,
Registered {
re_register_in : Pin < Box < tokio ::time ::Sleep > > ,
} ,
}
pub struct Behaviour {
inner : libp2p ::rendezvous ::Rendezvous ,
rendezvous_point : Multiaddr ,
rendezvous_peer_id : PeerId ,
namespace : XmrBtcNamespace ,
registration_status : RegistrationStatus ,
connection_status : ConnectionStatus ,
registration_ttl : Option < u64 > ,
}
impl Behaviour {
pub fn new (
identity : identity ::Keypair ,
rendezvous_peer_id : PeerId ,
rendezvous_address : Multiaddr ,
namespace : XmrBtcNamespace ,
registration_ttl : Option < u64 > ,
) -> Self {
Self {
inner : libp2p ::rendezvous ::Rendezvous ::new (
identity ,
libp2p ::rendezvous ::Config ::default ( ) ,
) ,
rendezvous_point : rendezvous_address ,
rendezvous_peer_id ,
namespace ,
registration_status : RegistrationStatus ::RegisterOnNextConnection ,
connection_status : ConnectionStatus ::Disconnected ,
registration_ttl ,
}
}
fn register ( & mut self ) {
self . inner . register (
self . namespace . into ( ) ,
self . rendezvous_peer_id ,
self . registration_ttl ,
) ;
}
}
impl NetworkBehaviour for Behaviour {
type ProtocolsHandler =
< libp2p ::rendezvous ::Rendezvous as NetworkBehaviour > ::ProtocolsHandler ;
type OutEvent = libp2p ::rendezvous ::Event ;
fn new_handler ( & mut self ) -> Self ::ProtocolsHandler {
self . inner . new_handler ( )
}
fn addresses_of_peer ( & mut self , peer_id : & PeerId ) -> Vec < Multiaddr > {
if peer_id = = & self . rendezvous_peer_id {
return vec! [ self . rendezvous_point . clone ( ) ] ;
}
vec! [ ]
}
fn inject_connected ( & mut self , peer_id : & PeerId ) {
if peer_id = = & self . rendezvous_peer_id {
self . connection_status = ConnectionStatus ::Connected ;
match & self . registration_status {
RegistrationStatus ::RegisterOnNextConnection = > {
self . register ( ) ;
self . registration_status = RegistrationStatus ::Pending ;
}
RegistrationStatus ::Registered { .. } = > { }
RegistrationStatus ::Pending = > { }
}
}
}
fn inject_disconnected ( & mut self , peer_id : & PeerId ) {
if peer_id = = & self . rendezvous_peer_id {
self . connection_status = ConnectionStatus ::Disconnected ;
}
}
fn inject_event (
& mut self ,
peer_id : PeerId ,
connection : ConnectionId ,
event : < < Self ::ProtocolsHandler as IntoProtocolsHandler > ::Handler as ProtocolsHandler > ::OutEvent ,
) {
self . inner . inject_event ( peer_id , connection , event )
}
fn inject_dial_failure ( & mut self , peer_id : & PeerId ) {
if peer_id = = & self . rendezvous_peer_id {
self . connection_status = ConnectionStatus ::Disconnected ;
}
}
#[ allow(clippy::type_complexity) ]
fn poll ( & mut self , cx : & mut std ::task ::Context < ' _ > , params : & mut impl PollParameters ) -> Poll < NetworkBehaviourAction < < < Self ::ProtocolsHandler as IntoProtocolsHandler > ::Handler as ProtocolsHandler > ::InEvent , Self ::OutEvent > > {
match & mut self . registration_status {
RegistrationStatus ::RegisterOnNextConnection = > match self . connection_status {
ConnectionStatus ::Disconnected = > {
self . connection_status = ConnectionStatus ::Dialling ;
return Poll ::Ready ( NetworkBehaviourAction ::DialPeer {
peer_id : self . rendezvous_peer_id ,
condition : DialPeerCondition ::Disconnected ,
} ) ;
}
ConnectionStatus ::Dialling = > { }
ConnectionStatus ::Connected = > {
self . registration_status = RegistrationStatus ::Pending ;
self . register ( ) ;
}
} ,
RegistrationStatus ::Registered { re_register_in } = > {
if let Poll ::Ready ( ( ) ) = re_register_in . poll_unpin ( cx ) {
match self . connection_status {
ConnectionStatus ::Connected = > {
self . registration_status = RegistrationStatus ::Pending ;
self . register ( ) ;
}
ConnectionStatus ::Disconnected = > {
self . registration_status =
RegistrationStatus ::RegisterOnNextConnection ;
return Poll ::Ready ( NetworkBehaviourAction ::DialPeer {
peer_id : self . rendezvous_peer_id ,
condition : DialPeerCondition ::Disconnected ,
} ) ;
}
ConnectionStatus ::Dialling = > { }
}
}
}
RegistrationStatus ::Pending = > { }
}
let inner_poll = self . inner . poll ( cx , params ) ;
// reset the timer if we successfully registered
if let Poll ::Ready ( NetworkBehaviourAction ::GenerateEvent (
libp2p ::rendezvous ::Event ::Registered { ttl , .. } ,
) ) = & inner_poll
{
let half_of_ttl = Duration ::from_secs ( * ttl ) / 2 ;
self . registration_status = RegistrationStatus ::Registered {
re_register_in : Box ::pin ( tokio ::time ::sleep ( half_of_ttl ) ) ,
} ;
}
inner_poll
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use crate ::network ::test ::{ new_swarm , SwarmExt } ;
use futures ::StreamExt ;
use libp2p ::swarm ::SwarmEvent ;
#[ tokio::test ]
async fn given_no_initial_connection_when_constructed_asb_connects_and_registers_with_rendezvous_node (
) {
let mut rendezvous_node = new_swarm ( | _ , identity | {
libp2p ::rendezvous ::Rendezvous ::new ( identity , libp2p ::rendezvous ::Config ::default ( ) )
} ) ;
let rendezvous_address = rendezvous_node . listen_on_random_memory_address ( ) . await ;
let mut asb = new_swarm ( | _ , identity | {
rendezous ::Behaviour ::new (
identity ,
* rendezvous_node . local_peer_id ( ) ,
rendezvous_address ,
XmrBtcNamespace ::Testnet ,
None ,
)
} ) ;
asb . listen_on_random_memory_address ( ) . await ; // this adds an external address
tokio ::spawn ( async move {
loop {
rendezvous_node . next ( ) . await ;
}
} ) ;
let asb_registered = tokio ::spawn ( async move {
loop {
if let SwarmEvent ::Behaviour ( libp2p ::rendezvous ::Event ::Registered { .. } ) =
asb . select_next_some ( ) . await
{
break ;
}
}
} ) ;
tokio ::time ::timeout ( Duration ::from_secs ( 10 ) , asb_registered )
. await
. unwrap ( )
. unwrap ( ) ;
}
#[ tokio::test ]
async fn asb_automatically_re_registers ( ) {
let min_ttl = 5 ;
let mut rendezvous_node = new_swarm ( | _ , identity | {
libp2p ::rendezvous ::Rendezvous ::new (
identity ,
libp2p ::rendezvous ::Config ::default ( ) . with_min_ttl ( min_ttl ) ,
)
} ) ;
let rendezvous_address = rendezvous_node . listen_on_random_memory_address ( ) . await ;
let mut asb = new_swarm ( | _ , identity | {
rendezous ::Behaviour ::new (
identity ,
* rendezvous_node . local_peer_id ( ) ,
rendezvous_address ,
XmrBtcNamespace ::Testnet ,
Some ( 5 ) ,
)
} ) ;
asb . listen_on_random_memory_address ( ) . await ; // this adds an external address
tokio ::spawn ( async move {
loop {
rendezvous_node . next ( ) . await ;
}
} ) ;
let asb_registered_three_times = tokio ::spawn ( async move {
let mut number_of_registrations = 0 ;
loop {
if let SwarmEvent ::Behaviour ( libp2p ::rendezvous ::Event ::Registered { .. } ) =
asb . select_next_some ( ) . await
{
number_of_registrations + = 1
}
if number_of_registrations = = 3 {
break ;
}
}
} ) ;
tokio ::time ::timeout ( Duration ::from_secs ( 30 ) , asb_registered_three_times )
. await
. unwrap ( )
. unwrap ( ) ;
}
}
}