Refactor config to use proc macros over builder#703
Conversation
3bad990 to
0c78caa
Compare
|
NOTE: This commit allowed the cli to pass multiple OHTTP relays as an arg so I needed to rebase + resolve conflicts. |
0c78caa to
5cf73aa
Compare
|
@DanGould I may need your advice with getting CI to pass. I ran I'm now running commands in this order: nix develop # for rust nightly shell
./payjoin-cli/contrib/test.sh # I checked docker is running, all passes
./payjoin-cli/contrib/lint.sh # passes
./payjoin-cli/contrib/rustfmt # seems to hang infinitely?I'm unsure what set of commands I have to run locally to be confident that CI will pass. I'll also note that it may be worthwhile to setup pre-commit hooks via something like husky for anything that we expect to happen before a commit, instead of relying on the developer to know which commands to use. Consistently pushing -> seeing a failing CI -> resetting + recommitting is tedious (I'm probably missing something and this isn't what others are having to do). |
|
FWIW e2e tests are failing for me locally, so CI appears to be sane.
There is no
These may pass, but the top level test is
You can also use Even easier than adding a dep, just set up a local pre-commit git hook script. That's what I have on my local repo at #!/usr/bin/env bash
set -euo pipefail
# -------- 1. Rustfmt (nightly toolchain) --------
echo "▶ cargo +nightly fmt --check"
cargo +nightly fmt --all -- --check
# -------- 2. Project-specific linter --------
echo "▶ ./contrib/lint.sh"
./contrib/lint.sh
# -------- 3. Fast local test suite --------
echo "▶ ./contrib/test_local.sh"
./contrib/test_local.sh
echo "✓ Pre-commit hook passed" |
f9b7ac3 to
2709877
Compare
|
Thank you for the help. Interesting that you're failing CI locally, everything is still passing even when running the top-level cargo 1.85.0-nightly (d73d2caf9 2024-12-31)I'm also rebased with the latest commit: |
|
1.85.0 is not the current nightly (which is 1.89.0 as of writing https://doc.rust-lang.org/nightly/nightly-rustc/cargo/) The flake needs to be kept up to date and has many open issues. It's apparently not a reliable dev environment yet. |
49fe8d8 to
f8766de
Compare
Pull Request Test Coverage Report for Build 15351670294Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
| if cli.flags.bip78.unwrap_or(false) { | ||
| if selected_version.is_some() { | ||
| return Err(ConfigError::Message( | ||
| "Multiple version flags specified. Please use only one of: --bip77, --bip78" |
There was a problem hiding this comment.
I aimed to not change any language or behavior here, but the difference between the v1/v2 features and bip77/bip78 would be helpful. Is bip77 a "version" flag? If not, I can update the language in another PR.
| // Set default values | ||
| let config = config | ||
| .set_default("bitcoind.rpchost", "http://localhost:18443")? | ||
| .set_override_option( | ||
| "bitcoind.rpchost", | ||
| matches.get_one::<Url>("rpchost").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.cookie", None::<String>)? | ||
| .set_override_option( | ||
| "bitcoind.cookie", | ||
| matches.get_one::<String>("cookie_file").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.rpcuser", "bitcoin")? | ||
| .set_override_option( | ||
| "bitcoind.rpcuser", | ||
| matches.get_one::<String>("rpcuser").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.rpcpassword", "")? | ||
| .set_override_option( | ||
| "bitcoind.rpcpassword", | ||
| matches.get_one::<String>("rpcpassword").map(|s| s.as_str()), | ||
| ) | ||
| .set_default("bitcoind.rpcpassword", "")?; |
There was a problem hiding this comment.
The missing defaults were what was causing the test failures before, so the tests caught a behavioral change I made. It didn't really make sense to me to provide defaults for these since rpcuser and rpcpassword don't have sensible defaults the way rpchost does, so it felt weird to give one here. I can take a look at refactoring these out of the tests later if we want.
There was a problem hiding this comment.
good issue to follow up with. The way you solved it for now works since it doesn't regress.
|
@DanGould These are passing CI now |
DanGould
left a comment
There was a problem hiding this comment.
Neat and tidy. So good to see this pass.
May you please add a commit that uses the Version enum for the determine_version function, and another to remove the unwrap() there in the return that could panic. This is my first pass review to unblock progress on this, but I want to take a closer second pass over the use of feature flags and shorthand rpc names to make sure everything matches up. I think our tests have very good coverage for the feature flags at least, and don't forsee problems on the second pass.
| // Set default values | ||
| let config = config | ||
| .set_default("bitcoind.rpchost", "http://localhost:18443")? | ||
| .set_override_option( | ||
| "bitcoind.rpchost", | ||
| matches.get_one::<Url>("rpchost").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.cookie", None::<String>)? | ||
| .set_override_option( | ||
| "bitcoind.cookie", | ||
| matches.get_one::<String>("cookie_file").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.rpcuser", "bitcoin")? | ||
| .set_override_option( | ||
| "bitcoind.rpcuser", | ||
| matches.get_one::<String>("rpcuser").map(|s| s.as_str()), | ||
| )? | ||
| .set_default("bitcoind.rpcpassword", "")? | ||
| .set_override_option( | ||
| "bitcoind.rpcpassword", | ||
| matches.get_one::<String>("rpcpassword").map(|s| s.as_str()), | ||
| ) | ||
| .set_default("bitcoind.rpcpassword", "")?; |
There was a problem hiding this comment.
good issue to follow up with. The way you solved it for now works since it doesn't regress.
f8766de to
c75d61d
Compare
Refactors the code to use proc macros over the "legacy" builder method as per `clap`'s recommendation. The macros allow the code to be more maintainable, readable, and reusable moving forward. See [here](https://docs.rs/clap/latest/clap/_faq/index.html#when-should-i-use-the-builder-vs-derive-apis) for more details on proc macros vs builder method rationales. Discussion preceding this PR in payjoin#684 and [here](#1)
Use the `Version` enum for the `determine_version` function instead of `u8`'s.
Removes `return Ok(selected_version.unwrap())` in favor of short circuiting to check if the version is `Some()` explicitly to avoid potential panics caused by the `.unwrap()`.
123cd1c to
5537853
Compare
payjoin-cli/src/main.rs
Outdated
| let fee = fee_rate | ||
| .unwrap_or_else(|| FeeRate::from_sat_per_vb(2).unwrap_or(FeeRate::BROADCAST_MIN)); |
There was a problem hiding this comment.
Is this a new default? It also seems like from_sat_per_vb(2) unwraps the Broadcast min (which will never get hit. I don't think we want this behavior change.
This should error if it's missing, not default.
|
Getting failure running RUST_LOG=debug cargo run --bin payjoin-cli -- --ohttp-relays="https://pj.benalleng.com,https://pj.bobspacebkk.com" receive 22222which results in [2025-05-29T21:52:47Z DEBUG payjoin_cli::app::config] App config: Config { db_path: "payjoin.sled", max_fee_rate: Some(FeeRate(2)), bitcoind: BitcoindConfig { rpchost: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("localhost")), port: Some(38332), path: "/wallet/alice", query: None, fragment: None }, cookie: Some("/Users/dan/f/Mutinynet/signet/.cookie"), rpcuser: "bitcoin", rpcpassword: "" }, version: Some(V2(V2Config { ohttp_keys: None, ohttp_relays: [Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("pj.benalleng.com,https")), port: None, path: "//pj.bobspacebkk.com", query: None, fragment: None }], pj_directory: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("payjo.in")), port: None, path: "/", query: None, fragment: None } })) }
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iterator] ordering before clearing tears: {0: 0}, max_header_stable_lsn: 0
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iterator] in clean_tail_tears, found missing item in tail: None and we'll scan segments {0: 0} above lowest lsn 0
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iterator] filtering out segments after detected tear at (lsn, lid) 7892
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iterator] hit max_lsn 7892 in iterator, stopping
[2025-05-29T21:52:47Z DEBUG sled::pagecache::snapshot] zeroing the end of the recovered segment at lsn 0 between lids 7893 and 524287
[2025-05-29T21:52:47Z DEBUG sled::pagecache::blob_io] gc_blobs removing any blob with an lsn above 7893
[2025-05-29T21:52:47Z DEBUG sled::pagecache::segment] SA starting with tip 524288 stable -1 free {}
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iobuf] starting log at recovered active offset 7893, recovered lsn 7893
[2025-05-29T21:52:47Z DEBUG sled::pagecache::iobuf] starting IoBufs with next_lsn: 7893 next_lid: 7893
[2025-05-29T21:52:47Z DEBUG sled::pagecache] load_snapshot loading pages from 0..6
[2025-05-29T21:52:47Z DEBUG bitcoincore_rpc] JSON-RPC request: getblockchaininfo []
[2025-05-29T21:52:47Z DEBUG bitcoincore_rpc] JSON-RPC request: getnetworkinfo []
[2025-05-29T21:52:47Z DEBUG bitcoincore_rpc] JSON-RPC request: getnewaddress [null,null]
[2025-05-29T21:52:47Z DEBUG bitcoincore_rpc] JSON-RPC request: getblockchaininfo []
[2025-05-29T21:52:47Z DEBUG bitcoincore_rpc] JSON-RPC request: getnetworkinfo []
Bootstrapping private network transport over Oblivious HTTP
[2025-05-29T21:52:47Z DEBUG reqwest::connect] starting new connection: https://payjo.in/
[2025-05-29T21:52:47Z DEBUG reqwest::connect] proxy(https://pj.benalleng.com,https) intercepts 'https://payjo.in/'
[2025-05-29T21:52:47Z DEBUG payjoin_cli::app::v2::ohttp] Failed to connect to relay: https://pj.benalleng.com,https//pj.bobspacebkk.com, Internal(InternalError(Reqwest(reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("payjo.in")), port: None, path: "/.well-known/ohttp-gateway", query: None, fragment: None }, source: hyper_util::client::legacy::Error(Connect, Custom { kind: Other, error: "invalid dnsname" }) })))
[2025-05-29T21:52:47Z DEBUG sled::pagecache::logger] IoBufs dropped
Error: No valid relays availablefrom this tip which otherwise succeeds on master (16f0c20) Edit: It wouldn't hurt to include a failure ohttp-relay to test this behavior in e2e test by passing the valid url & "https://example.com" |
payjoin-cli/src/cli/mod.rs
Outdated
| #[arg(long = "ohttp-relays", help = "One or more ohttp relay URLs, comma-separated", value_parser = value_parser!(Url))] | ||
| pub ohttp_relays: Option<Vec<Url>>, |
There was a problem hiding this comment.
| #[arg(long = "ohttp-relays", help = "One or more ohttp relay URLs, comma-separated", value_parser = value_parser!(Url))] | |
| pub ohttp_relays: Option<Vec<Url>>, | |
| #[arg(long = "ohttp-relays", help = "One or more ohttp relay URLs, comma-separated", value_parser = value_parser!(Url), value_delimiter = ',', action = clap::ArgAction::Append)] | |
| pub ohttp_relays: Option<Vec<Url>>, |
I think this returns to previously working behavior
DanGould
left a comment
There was a problem hiding this comment.
Thanks for the updates. I did a second thorough pass of each and every line, checking help strings etc. I think there are 2 more changes to be made.
- Don't default a minfeerate. Especially not of 2 sat/vB. That'll have people spending more than they anticipate. Please error as was the prior behavior.
- Add my suggestion and verify that enumerated ohttp-relays separated by commas work in
--ohttp-relayslike howcargo --featuresworks.
| #[arg( | ||
| long = "cookie-file", | ||
| short = 'c', | ||
| num_args(1), |
There was a problem hiding this comment.
I think num_args(1) is default behavior for ArgActions::Set and may be elided in a follow up PR. Thanks for keeping it to duplicatae the behavior for now.
| long, | ||
| short = 'r', | ||
| num_args(1), | ||
| help = "The URL of the Bitcoin RPC host, e.g. regtest default is http://localhost:18443" |
There was a problem hiding this comment.
good help clarification here.
Passing multiple `ohttp-relays` from the CLI was failing due to missing `value_delimiter = ','` and `clap::ArgAction::Append` to the proc macro.
`--fee-rate` was given a default when it shouldn't have been. This reverts that change.
|
Thanks for the feedback and tips @DanGould. I've implemented those final changes and tested them:
RUST_LOG=debug cargo run --bin payjoin-cli -- --ohttp-relays="https://pj.benalleng.com" send "bitcoin:bcrt1qhg9dsqcmle93ek7wlr7vqhgl9vsd0rek4ufclc?amount=0.00022222&pj=HTTPS://PAYJO.IN/K0ANSDFKYDAWX%23RK1QDL98KV7FZKYL7EYDQMWHR25ZMMFSTY82KWD5YVDKLGDZNY83JWDK+OH1QYP87E2AVMDKXDTU6R25WCPQ5ZUF02XHNPA65JMD8ZA2W4YRQN6UUWG+EX1556RK6Q"
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `/home/blu/src/rust-payjoin/target/debug/payjoin-cli '--ohttp-relays=https://pj.benalleng.com' send 'bitcoin:bcrt1qhg9dsqcmle93ek7wlr7vqhgl9vsd0rek4ufclc?amount=0.00022222&pj=HTTPS://PAYJO.IN/K0ANSDFKYDAWX%23RK1QDL98KV7FZKYL7EYDQMWHR25ZMMFSTY82KWD5YVDKLGDZNY83JWDK+OH1QYP87E2AVMDKXDTU6R25WCPQ5ZUF02XHNPA65JMD8ZA2W4YRQN6UUWG+EX1556RK6Q'`
error: The following required arguments were not provided:
--fee-rate <FEE_RATE>
Usage: payjoin-cli send --fee-rate <FEE_RATE> <BIP21>
|
There was a problem hiding this comment.
tACK c666882
OK for this time but next time fixes within a PR must amend the commit they fix. It doesn't make sense to commit mistakes and then follow with "Fix" commits in the same PR when you still have the power to make a clean history.
You can do this with minimal intrusion using git rebase -i <branch> and then fixup (or f for short) the commits into the relevant parent where the mistakes were made.
Thanks for the review. Will do in future. |
Refactors the code to use proc macros over the "legacy" builder method as per
clap's recommendation.The macros allow the code to be more maintainable, readable, and reusable moving forward.
See
here for more details on proc macro vs builder method rationales.
Discussion preceding this PR in #684 and here