feat(asb): Add registration-status controller cmd (#671)

* feat(asb): Add registration-status controller cmd

* fmt

* add changelog entry

* condense

* remove redundant attributes

* rename

* add comment
This commit is contained in:
Mohan 2025-11-02 23:13:28 +01:00 committed by GitHub
parent 7ca9c1442c
commit 1026a51c98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 159 additions and 4 deletions

View file

@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- ASB + CONTROLLER: Add a `registration-status` command to the controller shell. You can use it to get the registration status of the ASB at the configured rendezvous points.
- ASB + GUI + CLI + SWAP: Split high-verbosity tracing into separate hourly-rotating JSON log files per subsystem to reduce noise and aid debugging: `tracing*.log` (core things), `tracing-tor*.log` (purely tor related), `tracing-libp2p*.log` (low level networking), `tracing-monero-wallet*.log` (low level Monero wallet related). `swap-all.log` remains for non-verbose logs.
- ASB: Fix an issue where we would not redeem the Bitcoin and force a refund even though it was still possible to do so.

View file

@ -80,7 +80,11 @@ swap:
# Run the asb on testnet
asb-testnet:
ASB_DEV_ADDR_OUTPUT_PATH="$(pwd)/src-gui/.env.development" cargo run -p swap-asb --bin asb -- --trace --testnet start --rpc-bind-port 9944 --rpc-bind-host 0.0.0.0
ASB_DEV_ADDR_OUTPUT_PATH="$(pwd)/src-gui/.env.development" cargo run -p swap-asb --bin asb -- --testnet start --rpc-bind-port 9944 --rpc-bind-host 0.0.0.0
# Launch the ASB controller REPL against a local testnet ASB instance
asb-testnet-controller:
cargo run -p swap-controller --bin asb-controller -- --url http://127.0.0.1:9944
# Updates our submodules (currently only Monero C++ codebase)
update_submodules:

View file

@ -33,6 +33,32 @@ pub struct ActiveConnectionsResponse {
pub connections: usize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RendezvousConnectionStatus {
Disconnected,
Dialling,
Connected,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RendezvousRegistrationStatus {
RegisterOnNextConnection,
Pending,
Registered,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationStatusItem {
pub address: String,
pub connection: RendezvousConnectionStatus,
pub registration: RendezvousRegistrationStatus,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationStatusResponse {
pub registrations: Vec<RegistrationStatusItem>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Swap {
pub id: String,
@ -65,4 +91,6 @@ pub trait AsbApi {
async fn active_connections(&self) -> Result<ActiveConnectionsResponse, ErrorObjectOwned>;
#[method(name = "get_swaps")]
async fn get_swaps(&self) -> Result<Vec<Swap>, ErrorObjectOwned>;
#[method(name = "registration_status")]
async fn registration_status(&self) -> Result<RegistrationStatusResponse, ErrorObjectOwned>;
}

View file

@ -33,4 +33,6 @@ pub enum Cmd {
ActiveConnections,
/// Get list of swaps
GetSwaps,
/// Show rendezvous registration status
RegistrationStatus,
}

View file

@ -84,6 +84,20 @@ async fn dispatch(cmd: Cmd, client: impl AsbApiClient) -> anyhow::Result<()> {
let response = client.bitcoin_seed().await?;
println!("Descriptor (BIP-0382) containing the private keys of the internal Bitcoin wallet: \n{}", response.descriptor);
}
Cmd::RegistrationStatus => {
let response = client.registration_status().await?;
println!("Your asb registers at rendezvous to make itself discoverable to takers.\n");
if response.registrations.is_empty() {
println!("No rendezvous points configured");
} else {
for item in response.registrations {
println!(
"Connection status to rendezvous point at \"{}\" is \"{:?}\". Registration status is \"{:?}\"",
item.address, item.connection, item.registration
);
}
}
}
}
Ok(())
}

View file

@ -62,8 +62,8 @@ pub mod register {
use std::task::{Context, Poll};
use std::time::Duration;
#[derive(Clone, PartialEq)]
enum ConnectionStatus {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionStatus {
Disconnected,
Dialling,
Connected,
@ -86,6 +86,45 @@ pub mod register {
backoffs: HashMap<PeerId, ExponentialBackoff>,
}
// Provide a read-only snapshot of rendezvous registrations
impl Behaviour {
/// Returns a snapshot of registration and connection status for all configured rendezvous nodes.
pub fn registrations(&self) -> Vec<RegistrationReport> {
self.rendezvous_nodes
.iter()
.map(|n| RegistrationReport {
address: n.address.clone(),
connection: n.connection_status,
registration: match &n.registration_status {
RegistrationStatus::RegisterOnNextConnection => {
RegistrationStatusReport::RegisterOnNextConnection
}
RegistrationStatus::Pending => RegistrationStatusReport::Pending,
RegistrationStatus::Registered { .. } => {
RegistrationStatusReport::Registered
}
},
})
.collect()
}
}
/// Public representation of a rendezvous node registration status
/// The raw `RegistrationStatus` cannot be exposed because it is not serializable
#[derive(Debug, Clone)]
pub struct RegistrationReport {
pub address: Multiaddr,
pub connection: ConnectionStatus,
pub registration: RegistrationStatusReport,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegistrationStatusReport {
RegisterOnNextConnection,
Pending,
Registered,
}
/// A node running the rendezvous server protocol.
pub struct RendezvousNode {
pub address: Multiaddr,

View file

@ -523,6 +523,17 @@ where
let count = self.swarm.connected_peers().count();
let _ = respond_to.send(count);
}
EventLoopRequest::GetRegistrationStatus { respond_to } => {
let registrations = self
.swarm
.behaviour()
.rendezvous
.as_ref()
.map(|b| b.registrations())
.unwrap_or_default(); // If rendezvous behaviour is disabled we report empty list
let _ = respond_to.send(registrations);
}
}
}
}
@ -829,6 +840,9 @@ mod service {
GetActiveConnections {
respond_to: oneshot::Sender<usize>,
},
GetRegistrationStatus {
respond_to: oneshot::Sender<Vec<crate::asb::register::RegistrationReport>>,
},
}
/// Tower service for communicating with the EventLoop
@ -861,6 +875,18 @@ mod service {
rx.await
.map_err(|_| anyhow::anyhow!("EventLoop service did not respond"))
}
/// Get the registration status at configured rendezvous points
pub async fn get_registration_status(
&self,
) -> anyhow::Result<Vec<crate::asb::register::RegistrationReport>> {
let (tx, rx) = oneshot::channel();
self.sender
.send(EventLoopRequest::GetRegistrationStatus { respond_to: tx })
.map_err(|_| anyhow::anyhow!("EventLoop service is down"))?;
rx.await
.map_err(|_| anyhow::anyhow!("EventLoop service did not respond"))
}
}
}

View file

@ -9,7 +9,9 @@ use jsonrpsee::types::ErrorObjectOwned;
use std::sync::Arc;
use swap_controller_api::{
ActiveConnectionsResponse, AsbApiServer, BitcoinBalanceResponse, BitcoinSeedResponse,
MoneroAddressResponse, MoneroBalanceResponse, MoneroSeedResponse, MultiaddressesResponse, Swap,
MoneroAddressResponse, MoneroBalanceResponse, MoneroSeedResponse, MultiaddressesResponse,
RegistrationStatusItem, RegistrationStatusResponse, RendezvousConnectionStatus,
RendezvousRegistrationStatus, Swap,
};
use tokio_util::task::AbortOnDropHandle;
@ -159,6 +161,45 @@ impl AsbApiServer for RpcImpl {
Ok(swaps)
}
async fn registration_status(&self) -> Result<RegistrationStatusResponse, ErrorObjectOwned> {
let regs = self
.event_loop_service
.get_registration_status()
.await
.into_json_rpc_result()?;
let registrations = regs
.into_iter()
.map(|r| RegistrationStatusItem {
address: r.address.to_string(),
connection: match r.connection {
crate::asb::register::ConnectionStatus::Disconnected => {
RendezvousConnectionStatus::Disconnected
}
crate::asb::register::ConnectionStatus::Dialling => {
RendezvousConnectionStatus::Dialling
}
crate::asb::register::ConnectionStatus::Connected => {
RendezvousConnectionStatus::Connected
}
},
registration: match r.registration {
crate::asb::register::RegistrationStatusReport::RegisterOnNextConnection => {
RendezvousRegistrationStatus::RegisterOnNextConnection
}
crate::asb::register::RegistrationStatusReport::Pending => {
RendezvousRegistrationStatus::Pending
}
crate::asb::register::RegistrationStatusReport::Registered => {
RendezvousRegistrationStatus::Registered
}
},
})
.collect();
Ok(RegistrationStatusResponse { registrations })
}
}
trait IntoJsonRpcResult<T> {