Problem
user_identity is a single global field on TranslatorConfig (config.rs:40). When the translator falls back to a secondary upstream, it sends the same identity string — built for the primary pool — to every pool in the list.
Different pools enforce different identity conventions:
- SRI uses
sri/solo/<btc_address> (slash-delimited, no suffix)
- BlitzPool expects
<btc_address>.<worker_name> (dot-delimited with worker suffix)
When SRI is the primary and BlitzPool is the fallback, the fallback connection sends sri/solo/<addr> to BlitzPool. BlitzPool immediately closes the socket or returns OpenMiningChannelError(unknown-user), making the fallback always fail — even after the writer-task fix lands.
This was reproduced locally and confirmed by logs shared on sv2-ui PR #119.
Root cause
config.rs lines 64–72 — The Upstream struct has no user_identity field:
pub struct Upstream {
pub address: String,
pub port: u16,
pub authority_pubkey: Secp256k1PublicKey,
// no user_identity
}
sv1/sv1_server/mod.rs lines 943–946 — Identity is read from the global config, not from the active upstream:
let user_identity = if self.config.user_identity.starts_with("sri/") {
self.config.user_identity.clone()
} else {
format!("{}.miner{}", self.config.user_identity, miner_id)
};
This means whichever upstream is active, it always receives the identity built for the primary.
Proposed fix
1. Add optional user_identity to the Upstream struct (config.rs)
pub struct Upstream {
pub address: String,
pub port: u16,
pub authority_pubkey: Secp256k1PublicKey,
pub user_identity: Option<String>, // overrides global if set
}
TOML stays backward-compatible — user_identity is optional per upstream and falls back to the global value if not set:
user_identity = "default-identity" # global fallback
[[upstreams]]
address = "75.119.150.111"
port = 3333
authority_pubkey = "9auq..."
user_identity = "sri/solo/bc1q..." # SRI format for this upstream
[[upstreams]]
address = "blitzpool.yourdevice.ch"
port = 3333
authority_pubkey = "9bCo..."
user_identity = "bc1q....bitaxe" # BlitzPool format for this upstream
2. Thread the active upstream through to Sv1Server (lib/mod.rs)
initialize_upstream() already returns once a connection succeeds. It should also return the matched UpstreamEntry so the caller knows which upstream was selected. The fallback mechanism already recreates Sv1Server on each failover (lib/mod.rs:248), so passing the active upstream at construction time is a clean fit.
3. Use the per-upstream identity in sv1_server/mod.rs:943–946
let base_identity = active_upstream
.user_identity
.as_deref()
.unwrap_or(&self.config.user_identity);
let user_identity = if base_identity.starts_with("sri/") {
base_identity.to_string()
} else {
format!("{}.miner{}", base_identity, miner_id)
};
channel_manager does not need changes — identity flows through the OpenExtendedMiningChannel message already.
Without this fix, fallback between pools with different identity conventions always fails at the OpenExtendedMiningChannel step, silently crashing the translator after the fallback mechanism itself is triggered correctly.
Problem
user_identityis a single global field onTranslatorConfig(config.rs:40). When the translator falls back to a secondary upstream, it sends the same identity string — built for the primary pool — to every pool in the list.Different pools enforce different identity conventions:
sri/solo/<btc_address>(slash-delimited, no suffix)<btc_address>.<worker_name>(dot-delimited with worker suffix)When SRI is the primary and BlitzPool is the fallback, the fallback connection sends
sri/solo/<addr>to BlitzPool. BlitzPool immediately closes the socket or returnsOpenMiningChannelError(unknown-user), making the fallback always fail — even after the writer-task fix lands.This was reproduced locally and confirmed by logs shared on sv2-ui PR #119.
Root cause
config.rslines 64–72 — TheUpstreamstruct has nouser_identityfield:sv1/sv1_server/mod.rslines 943–946 — Identity is read from the global config, not from the active upstream:This means whichever upstream is active, it always receives the identity built for the primary.
Proposed fix
1. Add optional
user_identityto theUpstreamstruct (config.rs)TOML stays backward-compatible —
user_identityis optional per upstream and falls back to the global value if not set:2. Thread the active upstream through to
Sv1Server(lib/mod.rs)initialize_upstream()already returns once a connection succeeds. It should also return the matchedUpstreamEntryso the caller knows which upstream was selected. The fallback mechanism already recreatesSv1Serveron each failover (lib/mod.rs:248), so passing the active upstream at construction time is a clean fit.3. Use the per-upstream identity in
sv1_server/mod.rs:943–946channel_managerdoes not need changes — identity flows through theOpenExtendedMiningChannelmessage already.Without this fix, fallback between pools with different identity conventions always fails at the
OpenExtendedMiningChannelstep, silently crashing the translator after the fallback mechanism itself is triggered correctly.