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

payjoin / rust-payjoin / 12856536243

19 Jan 2025 07:30PM UTC coverage: 60.548% (+0.06%) from 60.489%
12856536243

Pull #491

github

web-flow
Merge 13bf515d5 into 4bbac3446
Pull Request #491: use FeeRate for max_fee_rate arg, and default it to BROADCAST_MIN

7 of 13 new or added lines in 6 files covered. (53.85%)

5 existing lines in 1 file now uncovered.

2939 of 4854 relevant lines covered (60.55%)

953.84 hits per line

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

0.0
/payjoin-cli/src/main.rs
1
use anyhow::{Context, Result};
2
use app::config::AppConfig;
3
use app::App as AppTrait;
4
use clap::{arg, value_parser, Arg, ArgMatches, Command};
5
use payjoin::bitcoin::amount::ParseAmountError;
6
use payjoin::bitcoin::{Amount, FeeRate};
7
use url::Url;
8

9
mod app;
10
mod db;
11

12
#[cfg(all(not(feature = "v2"), feature = "v1"))]
13
use app::v1::App;
14
#[cfg(feature = "v2")]
15
use app::v2::App;
16

17
#[tokio::main]
18
async fn main() -> Result<()> {
×
19
    env_logger::init();
×
20

×
21
    let matches = cli();
×
22
    let config = AppConfig::new(&matches).with_context(|| "Failed to parse config")?;
×
23
    let app = App::new(config)?;
×
24

×
25
    match matches.subcommand() {
×
26
        Some(("send", sub_matches)) => {
×
27
            let bip21 = sub_matches.get_one::<String>("BIP21").context("Missing BIP21 argument")?;
×
28
            let fee_rate = sub_matches
×
29
                .get_one::<FeeRate>("fee_rate")
×
30
                .context("Missing --fee-rate argument")?;
×
31
            app.send_payjoin(bip21, *fee_rate).await?;
×
32
        }
×
33
        Some(("receive", sub_matches)) => {
×
34
            let amount =
×
35
                sub_matches.get_one::<Amount>("AMOUNT").context("Missing AMOUNT argument")?;
×
36
            app.receive_payjoin(*amount).await?;
×
37
        }
×
38
        #[cfg(feature = "v2")]
×
39
        Some(("resume", _)) => {
×
40
            println!("resume");
×
41
            app.resume_payjoins().await?;
×
42
        }
×
43
        _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
×
44
    }
×
45

×
46
    Ok(())
×
47
}
×
48

49
fn cli() -> ArgMatches {
×
50
    let mut cmd = Command::new("payjoin")
×
51
        .version(env!("CARGO_PKG_VERSION"))
×
52
        .about("Payjoin - bitcoin scaling, savings, and privacy by default")
×
53
        .arg(
×
54
            Arg::new("rpchost")
×
55
                .long("rpchost")
×
56
                .short('r')
×
57
                .num_args(1)
×
58
                .help("The port of the bitcoin node")
×
59
                .value_parser(value_parser!(Url)),
×
60
        )
×
61
        .arg(
×
62
            Arg::new("cookie_file")
×
63
                .long("cookie-file")
×
64
                .short('c')
×
65
                .num_args(1)
×
66
                .help("Path to the cookie file of the bitcoin node"),
×
67
        )
×
68
        .arg(
×
69
            Arg::new("rpcuser")
×
70
                .long("rpcuser")
×
71
                .num_args(1)
×
72
                .help("The username for the bitcoin node"),
×
73
        )
×
74
        .arg(
×
75
            Arg::new("rpcpassword")
×
76
                .long("rpcpassword")
×
77
                .num_args(1)
×
78
                .help("The password for the bitcoin node"),
×
79
        )
×
80
        .arg(Arg::new("db_path").short('d').long("db-path").help("Sets a custom database path"))
×
81
        .subcommand_required(true);
×
82

×
83
    // Conditional arguments based on features
×
84
    #[cfg(feature = "v2")]
×
85
    {
×
86
        cmd = cmd.arg(
×
87
            Arg::new("ohttp_relay")
×
88
                .long("ohttp-relay")
×
89
                .help("The ohttp relay url")
×
90
                .value_parser(value_parser!(Url)),
×
91
        );
×
92
    }
×
93

94
    cmd = cmd.subcommand(
×
95
        Command::new("send")
×
96
            .arg_required_else_help(true)
×
97
            .arg(arg!(<BIP21> "The `bitcoin:...` payjoin uri to send to"))
×
98
            .arg_required_else_help(true)
×
99
            .arg(
×
100
                Arg::new("fee_rate")
×
101
                    .long("fee-rate")
×
102
                    .value_name("FEE_SAT_PER_VB")
×
103
                    .help("Fee rate in sat/vB")
×
104
                    .value_parser(parse_feerate_in_sat_per_vb),
×
105
            ),
×
106
    );
107

108
    let mut receive_cmd = Command::new("receive")
×
109
        .arg_required_else_help(true)
×
110
        .arg(arg!(<AMOUNT> "The amount to receive in satoshis").value_parser(parse_amount_in_sat))
×
111
        .arg_required_else_help(true);
×
112

×
113
    #[cfg(feature = "v2")]
×
114
    let mut cmd = cmd.subcommand(Command::new("resume"));
×
115

×
116
    // Conditional arguments based on features for the receive subcommand
×
117
    receive_cmd = receive_cmd.arg(
×
118
        Arg::new("max_fee_rate")
×
119
            .long("max-fee-rate")
×
120
            .num_args(1)
×
NEW
121
            .help("The maximum effective fee rate the receiver is willing to pay (in sat/vB)")
×
NEW
122
            .value_parser(parse_feerate_in_sat_per_vb),
×
123
    );
×
124
    #[cfg(not(feature = "v2"))]
×
125
    {
×
126
        receive_cmd = receive_cmd.arg(
×
127
            Arg::new("port")
×
128
                .long("port")
×
129
                .short('p')
×
130
                .num_args(1)
×
131
                .help("The local port to listen on"),
×
132
        );
×
133
        receive_cmd = receive_cmd.arg(
×
134
            Arg::new("pj_endpoint")
×
135
                .long("pj-endpoint")
×
136
                .short('e')
×
137
                .num_args(1)
×
138
                .help("The `pj=` endpoint to receive the payjoin request")
×
139
                .value_parser(value_parser!(Url)),
×
140
        );
×
141
    }
×
142

×
143
    #[cfg(feature = "v2")]
×
144
    {
×
145
        receive_cmd = receive_cmd.arg(
×
146
            Arg::new("pj_directory")
×
147
                .long("pj-directory")
×
148
                .num_args(1)
×
149
                .help("The directory to store payjoin requests")
×
150
                .value_parser(value_parser!(Url)),
×
151
        );
×
152
        receive_cmd = receive_cmd
×
153
            .arg(Arg::new("ohttp_keys").long("ohttp-keys").help("The ohttp key config file path"));
×
154
    }
×
155

×
156
    cmd = cmd.subcommand(receive_cmd);
×
157
    cmd.get_matches()
×
158
}
×
159

160
fn parse_amount_in_sat(s: &str) -> Result<Amount, ParseAmountError> {
×
161
    Amount::from_str_in(s, payjoin::bitcoin::Denomination::Satoshi)
×
162
}
×
163

164
fn parse_feerate_in_sat_per_vb(s: &str) -> Result<FeeRate, std::num::ParseFloatError> {
×
165
    let fee_rate_sat_per_vb: f32 = s.parse()?;
×
166
    let fee_rate_sat_per_kwu = fee_rate_sat_per_vb * 250.0_f32;
×
167
    Ok(FeeRate::from_sat_per_kwu(fee_rate_sat_per_kwu.ceil() as u64))
×
168
}
×
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