Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion src/api/bitcoind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,41 @@ fn probe_rpc(config: &MakerConfig) -> Option<String> {
Some(info.chain.to_string())
}

/// Try well-known default RPC ports for regtest and signet.
/// Returns the chain name on the first successful connection, or None if all fail.
fn probe_standard_ports() -> Option<String> {
use coinswap::bitcoind::bitcoincore_rpc::{Auth, Client, RpcApi};
use std::net::TcpStream;
use std::time::Duration;

let addresses = [
"127.0.0.1:18443", // regtest
"127.0.0.1:38332", // signet
];
// Localhost-only fallback credentials; not suitable for remote hosts.
let auth_methods = [Auth::UserPass("user".into(), "password".into()), Auth::None];

for address in &addresses {
let Ok(socket_addr) = address.parse() else {
continue;
};
// Fast TCP check to avoid blocking on OS connect timeouts (~30 s) for
// ports that are not listening or behind a packet filter.
if TcpStream::connect_timeout(&socket_addr, Duration::from_millis(300)).is_err() {
continue;
}
let url = format!("http://{}", address);
for auth in auth_methods.clone() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Avoid unnecessary array cloning in the loop.

The current code clones the entire auth_methods array (including owned String values from line 44) on every iteration of the outer loop. Iterate by reference instead and clone only when passing to Client::new.

♻️ Proposed fix
-    for auth in auth_methods.clone() {
-        if let Ok(client) = Client::new(&url, auth) {
+    for auth in &auth_methods {
+        if let Ok(client) = Client::new(&url, auth.clone()) {
             if let Ok(info) = client.get_blockchain_info() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for auth in auth_methods.clone() {
for auth in &auth_methods {
if let Ok(client) = Client::new(&url, auth.clone()) {
if let Ok(info) = client.get_blockchain_info() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/bitcoind.rs` at line 56, The loop is cloning the entire auth_methods
collection on each iteration causing unnecessary allocations; change the for
auth in auth_methods.clone() loop to iterate by reference (for auth in
&auth_methods) and only clone the individual auth when needed (e.g., pass
auth.clone() into Client::new), ensuring other uses use &auth to avoid extra
copies; update any uses inside the loop that expect owned String accordingly to
call .clone() at the point of Client::new.

if let Ok(client) = Client::new(&url, auth) {
if let Ok(info) = client.get_blockchain_info() {
return Some(info.chain.to_string());
}
}
}
}
None
}

/// Get bitcoind status by probing RPC connectivity via any registered maker's config.
/// Falls back to the dashboard-managed process state if no makers are configured.
#[utoipa::path(
Expand Down Expand Up @@ -56,7 +91,7 @@ async fn get_status(State(state): State<AppState>) -> Json<ApiResponse<BitcoindS
return Some(network);
}
}
None
probe_standard_ports()
})
.await
.unwrap_or(None);
Expand Down
17 changes: 13 additions & 4 deletions src/api/makers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,19 @@ async fn create_maker(
}

match mgr.create_maker(body.id.clone(), config) {
Ok(()) => (
StatusCode::CREATED,
Json(ApiResponse::ok(MakerInfo { id: body.id })),
),
Ok(()) => {
if let Err(e) = mgr.start_maker(&body.id) {
tracing::warn!(
"Maker '{}' created but failed to auto-start: {}",
body.id,
e
);
}
(
StatusCode::CREATED,
Json(ApiResponse::ok(MakerInfo { id: body.id })),
)
}
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ApiResponse::err(e.to_string())),
Expand Down
13 changes: 13 additions & 0 deletions src/maker_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ impl MakerManager {
mgr.persist();
}

// Auto-start all successfully restored makers
let restored_ids: Vec<MakerId> = mgr.configs.keys().cloned().collect();
for id in restored_ids {
if mgr.pool.contains(&id) {
match mgr.pool.start_server(&id) {
Ok(()) => tracing::info!("Maker '{}' auto-started on dashboard startup", id),
Err(e) => {
tracing::warn!("Maker '{}' restored but failed to auto-start: {}", id, e)
}
}
}
}

Ok(mgr)
}

Expand Down
6 changes: 1 addition & 5 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,11 +617,7 @@ fn test_maker_manager_integration() {
generate_blocks(&bitcoind, 1);
taker.get_wallet().write().unwrap().sync_and_save().unwrap();

// Start both makers and wait for their coinswap servers to be ready
println!("[INFO] Starting makers");
client.start_maker(MAKER_ALPHA_ID);
client.start_maker(MAKER_BETA_ID);

// Makers were auto-started on creation; wait for their coinswap servers to be ready.
// Start a continuous block generator. Makers broadcast fidelity bonds and
// settlement transactions asynchronously and then enter an internal sync
// loop that holds the wallet write lock with growing backoff (10s, 20s,
Expand Down
Loading