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

payjoin / rust-payjoin / 15282870970

27 May 2025 06:26PM UTC coverage: 84.138% (-0.4%) from 84.553%
15282870970

Pull #705

github

web-flow
Merge 8d5fa997b into 87042266d
Pull Request #705: Adjust _danger-local-https to be the default to prevent dangerous cert vulnerability when building with --all-features

5 of 14 new or added lines in 3 files covered. (35.71%)

40 existing lines in 3 files now uncovered.

6519 of 7748 relevant lines covered (84.14%)

597.8 hits per line

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

26.09
/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<payjoin::Url>,
10
    failed_relays: Vec<payjoin::Url>,
11
}
12

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

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

18
    pub fn get_selected_relay(&self) -> Option<payjoin::Url> { self.selected_relay.clone() }
6✔
19

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

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

25
pub(crate) async fn unwrap_ohttp_keys_or_else_fetch(
1✔
26
    config: &Config,
1✔
27
    relay_state: Arc<Mutex<RelayManager>>,
1✔
28
) -> Result<payjoin::OhttpKeys> {
1✔
29
    if let Some(keys) = config.v2()?.ohttp_keys.clone() {
1✔
30
        println!("Using OHTTP Keys from config");
1✔
31
        Ok(keys)
1✔
32
    } else {
33
        println!("Bootstrapping private network transport over Oblivious HTTP");
×
34

×
35
        fetch_keys(config, relay_state.clone())
×
36
            .await
×
37
            .and_then(|keys| keys.ok_or_else(|| anyhow::anyhow!("No OHTTP keys found")))
×
38
    }
39
}
1✔
40

UNCOV
41
async fn fetch_keys(
×
UNCOV
42
    config: &Config,
×
NEW
43
    relay_state: Arc<Mutex<RelayManager>>,
×
UNCOV
44
) -> Result<Option<payjoin::OhttpKeys>> {
×
45
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
UNCOV
46
    let payjoin_directory = config.v2()?.pj_directory.clone();
×
UNCOV
47
    let relays = config.v2()?.ohttp_relays.clone();
×
48

UNCOV
49
    loop {
×
UNCOV
50
        let failed_relays =
×
UNCOV
51
            relay_state.lock().expect("Lock should not be poisoned").get_failed_relays();
×
UNCOV
52

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

×
UNCOV
56
        if remaining_relays.is_empty() {
×
UNCOV
57
            return Err(anyhow!("No valid relays available"));
×
UNCOV
58
        }
×
59

UNCOV
60
        let selected_relay =
×
UNCOV
61
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
×
UNCOV
62
                Some(relay) => relay.clone(),
×
UNCOV
63
                None => return Err(anyhow!("Failed to select from remaining relays")),
×
64
            };
65

UNCOV
66
        relay_state
×
UNCOV
67
            .lock()
×
UNCOV
68
            .expect("Lock should not be poisoned")
×
UNCOV
69
            .set_selected_relay(selected_relay.clone());
×
70

71
        #[cfg(not(feature = "pki-https"))]
NEW
72
        let ohttp_keys = {
×
NEW
73
            let cert_der = crate::app::read_local_cert()?;
×
NEW
74
            payjoin::io::fetch_ohttp_keys_with_cert(
×
NEW
75
                selected_relay.clone(),
×
NEW
76
                payjoin_directory.clone(),
×
NEW
77
                cert_der,
×
NEW
78
            )
×
NEW
79
            .await
×
80
        };
81

82
        #[cfg(feature = "pki-https")]
83
        let ohttp_keys = {
84
            payjoin::io::fetch_ohttp_keys(selected_relay.clone(), payjoin_directory.clone()).await
85
        };
86

UNCOV
87
        match ohttp_keys {
×
UNCOV
88
            Ok(keys) => return Ok(Some(keys)),
×
UNCOV
89
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
×
UNCOV
90
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
×
91
            }
UNCOV
92
            Err(e) => {
×
UNCOV
93
                log::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
×
UNCOV
94
                relay_state
×
UNCOV
95
                    .lock()
×
UNCOV
96
                    .expect("Lock should not be poisoned")
×
UNCOV
97
                    .add_failed_relay(selected_relay);
×
98
            }
99
        }
100
    }
UNCOV
101
}
×
102

103
#[cfg(feature = "pki-https")]
104
pub(crate) async fn validate_relay(
105
    config: &Config,
106
    relay_state: Arc<Mutex<RelayManager>>,
107
) -> Result<payjoin::Url> {
108
    use payjoin::bitcoin::secp256k1::rand::prelude::SliceRandom;
109
    let payjoin_directory = config.v2()?.pj_directory.clone();
110
    let relays = config.v2()?.ohttp_relays.clone();
111

112
    loop {
113
        let failed_relays =
114
            relay_state.lock().expect("Lock should not be poisoned").get_failed_relays();
115

116
        let remaining_relays: Vec<_> =
117
            relays.iter().filter(|r| !failed_relays.contains(r)).cloned().collect();
118

119
        if remaining_relays.is_empty() {
120
            return Err(anyhow!("No valid relays available"));
121
        }
122

123
        let selected_relay =
124
            match remaining_relays.choose(&mut payjoin::bitcoin::key::rand::thread_rng()) {
125
                Some(relay) => relay.clone(),
126
                None => return Err(anyhow!("Failed to select from remaining relays")),
127
            };
128

129
        relay_state
130
            .lock()
131
            .expect("Lock should not be poisoned")
132
            .set_selected_relay(selected_relay.clone());
133

134
        let ohttp_keys =
135
            payjoin::io::fetch_ohttp_keys(selected_relay.clone(), payjoin_directory.clone()).await;
136

137
        match ohttp_keys {
138
            Ok(_) => return Ok(selected_relay),
139
            Err(payjoin::io::Error::UnexpectedStatusCode(e)) => {
140
                return Err(payjoin::io::Error::UnexpectedStatusCode(e).into());
141
            }
142
            Err(e) => {
143
                log::debug!("Failed to connect to relay: {selected_relay}, {e:?}");
144
                relay_state
145
                    .lock()
146
                    .expect("Lock should not be poisoned")
147
                    .add_failed_relay(selected_relay);
148
            }
149
        }
150
    }
151
}
152

153
#[cfg(not(feature = "pki-https"))]
154
pub(crate) async fn validate_relay(
6✔
155
    config: &Config,
6✔
156
    _relay_state: Arc<Mutex<RelayManager>>,
6✔
157
) -> Result<payjoin::Url> {
6✔
158
    let relay = config.v2()?.ohttp_relays.first().expect("no OHTTP relay set").clone();
6✔
159

6✔
160
    Ok(relay)
6✔
161
}
6✔
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