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

payjoin / rust-payjoin / 13637705402

03 Mar 2025 06:23PM UTC coverage: 79.725% (+0.4%) from 79.302%
13637705402

push

github

web-flow
Multiparty Senders: NS1R (#434)

Let a receiver batch sends from multiple peers into one transaction.

407 of 477 new or added lines in 9 files covered. (85.32%)

1 existing line in 1 file now uncovered.

4530 of 5682 relevant lines covered (79.73%)

812.66 hits per line

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

72.86
/payjoin/src/receive/optional_parameters.rs
1
use std::borrow::Borrow;
2
use std::fmt;
3

4
use bitcoin::FeeRate;
5
use log::warn;
6

7
#[derive(Debug, Clone)]
8
pub(crate) struct Params {
9
    // version
10
    pub v: usize,
11
    // disableoutputsubstitution
12
    pub disable_output_substitution: bool,
13
    // maxadditionalfeecontribution, additionalfeeoutputindex
14
    pub additional_fee_contribution: Option<(bitcoin::Amount, usize)>,
15
    // minfeerate
16
    pub min_fee_rate: FeeRate,
17
    #[cfg(feature = "_multiparty")]
18
    /// Opt in to optimistic psbt merge
19
    pub optimistic_merge: bool,
20
}
21

22
impl Default for Params {
23
    fn default() -> Self {
29✔
24
        Params {
29✔
25
            v: 1,
29✔
26
            disable_output_substitution: false,
29✔
27
            additional_fee_contribution: None,
29✔
28
            min_fee_rate: FeeRate::BROADCAST_MIN,
29✔
29
            #[cfg(feature = "_multiparty")]
29✔
30
            optimistic_merge: false,
29✔
31
        }
29✔
32
    }
29✔
33
}
34

35
impl Params {
36
    pub fn from_query_pairs<K, V, I>(
25✔
37
        pairs: I,
25✔
38
        supported_versions: &'static [usize],
25✔
39
    ) -> Result<Self, Error>
25✔
40
    where
25✔
41
        I: Iterator<Item = (K, V)>,
25✔
42
        K: Borrow<str> + Into<String>,
25✔
43
        V: Borrow<str> + Into<String>,
25✔
44
    {
25✔
45
        let mut params = Params::default();
25✔
46

25✔
47
        let mut additional_fee_output_index = None;
25✔
48
        let mut max_additional_fee_contribution = None;
25✔
49

50
        for (key, v) in pairs {
98✔
51
            match (key.borrow(), v.borrow()) {
73✔
52
                ("v", version) =>
73✔
53
                    params.v = match version.parse::<usize>() {
20✔
54
                        Ok(version) if supported_versions.contains(&version) => version,
20✔
55
                        _ => return Err(Error::UnknownVersion { supported_versions }),
×
56
                    },
57
                ("additionalfeeoutputindex", index) =>
53✔
58
                    additional_fee_output_index = match index.parse::<usize>() {
16✔
59
                        Ok(index) => Some(index),
16✔
60
                        Err(_error) => {
×
61
                            warn!(
×
62
                                "bad `additionalfeeoutputindex` query value '{}': {}",
×
63
                                index, _error
64
                            );
65
                            None
×
66
                        }
67
                    },
68
                ("maxadditionalfeecontribution", fee) =>
37✔
69
                    max_additional_fee_contribution =
16✔
70
                        match bitcoin::Amount::from_str_in(fee, bitcoin::Denomination::Satoshi) {
16✔
71
                            Ok(contribution) => Some(contribution),
16✔
72
                            Err(_error) => {
×
73
                                warn!(
×
74
                                    "bad `maxadditionalfeecontribution` query value '{}': {}",
×
75
                                    fee, _error
76
                                );
77
                                None
×
78
                            }
79
                        },
80
                ("minfeerate", fee_rate) =>
21✔
81
                    params.min_fee_rate = match fee_rate.parse::<f32>() {
13✔
82
                        Ok(fee_rate_sat_per_vb) => {
13✔
83
                            // TODO Parse with serde when rust-bitcoin supports it
13✔
84
                            let fee_rate_sat_per_kwu = fee_rate_sat_per_vb * 250.0_f32;
13✔
85
                            // since it's a minnimum, we want to round up
13✔
86
                            FeeRate::from_sat_per_kwu(fee_rate_sat_per_kwu.ceil() as u64)
13✔
87
                        }
88
                        Err(_) => return Err(Error::FeeRate),
×
89
                    },
90
                ("disableoutputsubstitution", v) =>
8✔
91
                    params.disable_output_substitution = v == "true",
×
92
                #[cfg(feature = "_multiparty")]
93
                ("optimisticmerge", v) => params.optimistic_merge = v == "true",
8✔
UNCOV
94
                _ => (),
×
95
            }
96
        }
97

98
        match (max_additional_fee_contribution, additional_fee_output_index) {
25✔
99
            (Some(amount), Some(index)) =>
16✔
100
                params.additional_fee_contribution = Some((amount, index)),
16✔
101
            (Some(_), None) | (None, Some(_)) => {
102
                warn!("only one additional-fee parameter specified: {:?}", params);
×
103
            }
104
            _ => (),
9✔
105
        }
106

107
        log::debug!("parsed optional parameters: {:?}", params);
25✔
108
        Ok(params)
25✔
109
    }
25✔
110
}
111

112
#[derive(Debug)]
113
pub(crate) enum Error {
114
    UnknownVersion { supported_versions: &'static [usize] },
115
    FeeRate,
116
}
117

118
impl fmt::Display for Error {
119
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
120
        match self {
×
121
            Error::UnknownVersion { .. } => write!(f, "unknown version"),
×
122
            Error::FeeRate => write!(f, "could not parse feerate"),
×
123
        }
124
    }
×
125
}
126

127
impl std::error::Error for Error {
128
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
×
129
}
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