• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 24105603780

07 Apr 2026 09:35PM UTC coverage: 79.263% (-5.1%) from 84.34%
24105603780

Pull #1469

github

web-flow
Merge f58dfdc7d into 637f9130c
Pull Request #1469: Add optional esplora wallet setup for payjoin-cli backend

189 of 364 new or added lines in 4 files covered. (51.92%)

529 existing lines in 9 files now uncovered.

10320 of 13020 relevant lines covered (79.26%)

396.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/payjoin-cli/src/app/v2/ohttp.rs
1
use std::sync::{Arc, Mutex};
2

3
use anyhow::{anyhow, Result};
4

5
use super::Config;
6

7
#[derive(Debug, Clone)]
8
pub struct RelayManager {
9
    selected_relay: Option<url::Url>,
10
    failed_relays: Vec<url::Url>,
11
}
12

13
impl RelayManager {
UNCOV
14
    pub fn new() -> Self { RelayManager { selected_relay: None, failed_relays: Vec::new() } }
×
15

UNCOV
16
    pub fn set_selected_relay(&mut self, relay: url::Url) { self.selected_relay = Some(relay); }
×
17

UNCOV
18
    pub fn get_selected_relay(&self) -> Option<url::Url> { self.selected_relay.clone() }
×
19

20
    pub fn add_failed_relay(&mut self, relay: url::Url) { self.failed_relays.push(relay); }
×
21

UNCOV
22
    pub fn get_failed_relays(&self) -> Vec<url::Url> { self.failed_relays.clone() }
×
23
}
24

25
pub(crate) struct ValidatedOhttpKeys {
26
    pub(crate) ohttp_keys: payjoin::OhttpKeys,
27
    pub(crate) relay_url: url::Url,
28
}
29

UNCOV
30
pub(crate) async fn unwrap_ohttp_keys_or_else_fetch(
×
UNCOV
31
    config: &Config,
×
UNCOV
32
    directory: Option<url::Url>,
×
UNCOV
33
    relay_manager: Arc<Mutex<RelayManager>>,
×
UNCOV
34
) -> Result<ValidatedOhttpKeys> {
×
UNCOV
35
    if let Some(ohttp_keys) = config.v2()?.ohttp_keys.clone() {
×
UNCOV
36
        println!("Using OHTTP Keys from config");
×
UNCOV
37
        let validated = fetch_ohttp_keys(config, directory, relay_manager).await?;
×
UNCOV
38
        Ok(ValidatedOhttpKeys { ohttp_keys, relay_url: validated.relay_url })
×
39
    } else {
UNCOV
40
        println!("Bootstrapping private network transport over Oblivious HTTP");
×
UNCOV
41
        let fetched_keys = fetch_ohttp_keys(config, directory, relay_manager).await?;
×
42

UNCOV
43
        Ok(fetched_keys)
×
44
    }
UNCOV
45
}
×
46

UNCOV
47
async fn fetch_ohttp_keys(
×
UNCOV
48
    config: &Config,
×
UNCOV
49
    directory: Option<url::Url>,
×
UNCOV
50
    relay_manager: Arc<Mutex<RelayManager>>,
×
UNCOV
51
) -> Result<ValidatedOhttpKeys> {
×
52
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
UNCOV
53
    let payjoin_directory = directory.unwrap_or(config.v2()?.pj_directory.clone());
×
UNCOV
54
    let relays = config.v2()?.ohttp_relays.clone();
×
55

56
    loop {
UNCOV
57
        let failed_relays =
×
UNCOV
58
            relay_manager.lock().expect("Lock should not be poisoned").get_failed_relays();
×
59

UNCOV
60
        let remaining_relays: Vec<_> =
×
UNCOV
61
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
×
62

UNCOV
63
        if remaining_relays.is_empty() {
×
64
            return Err(anyhow!("No valid relays available"));
×
UNCOV
65
        }
×
66

UNCOV
67
        let selected_relay =
×
UNCOV
68
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
×
UNCOV
69
                Some(relay) => relay.clone(),
×
70
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
71
            };
72

UNCOV
73
        relay_manager
×
UNCOV
74
            .lock()
×
UNCOV
75
            .expect("Lock should not be poisoned")
×
UNCOV
76
            .set_selected_relay(selected_relay.clone());
×
77

UNCOV
78
        let ohttp_keys = {
×
79
            #[cfg(feature = "_manual-tls")]
80
            {
UNCOV
81
                if let Some(cert_path) = config.root_certificate.as_ref() {
×
UNCOV
82
                    let cert_der = std::fs::read(cert_path)?;
×
UNCOV
83
                    payjoin::io::fetch_ohttp_keys_with_cert(
×
UNCOV
84
                        selected_relay.as_str(),
×
UNCOV
85
                        payjoin_directory.as_str(),
×
UNCOV
86
                        &cert_der,
×
UNCOV
87
                    )
×
UNCOV
88
                    .await
×
89
                } else {
90
                    payjoin::io::fetch_ohttp_keys(
×
91
                        selected_relay.as_str(),
×
92
                        payjoin_directory.as_str(),
×
93
                    )
×
94
                    .await
×
95
                }
96
            }
97
            #[cfg(not(feature = "_manual-tls"))]
98
            payjoin::io::fetch_ohttp_keys(selected_relay.as_str(), payjoin_directory.as_str()).await
99
        };
100

101
        match ohttp_keys {
×
UNCOV
102
            Ok(keys) =>
×
UNCOV
103
                return Ok(ValidatedOhttpKeys { ohttp_keys: keys, relay_url: selected_relay }),
×
104
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
105
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
106
            }
107
            Err(e) => {
×
108
                tracing::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
109
                relay_manager
×
110
                    .lock()
×
111
                    .expect("Lock should not be poisoned")
×
112
                    .add_failed_relay(selected_relay);
×
113
            }
114
        }
115
    }
UNCOV
116
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc