Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qr optimizations #417

Merged
merged 6 commits into from
Dec 2, 2024
Merged

Qr optimizations #417

merged 6 commits into from
Dec 2, 2024

Conversation

nothingmuch
Copy link
Collaborator

These commits:

  1. encode fragment paramters as bech32 strings (with no checksum), with 2 character param names in the human readable part, and + as a delimiter
    • expiry time parameter encoded as a little endian u32, in line with bitcoin header timestamp and unix time nLocktime encoding. EX hrp
    • ohttp has OH hrp, same byte representation as previously encoded in base64
    • receiver key has RK hrp, same byte representation as previously encoded in base64
  2. use bech32 with no human readable part for the subdirectory IDs
  3. uppercase the scheme and host of the URI
  4. move the pj so it follows the pjos parameter

Once the # %-escaping bug is fixed (see #373), and as long as no path elements between the host and the subdirectory ID, the entire pj parameter of the bip21 URI can be encoded in a QR using the alphanumeric mode. Closes #389.

I'm not sure about the last commit, although it seems sufficiently motivated since there is no purpose to clearing these values it doesn't actually simplify the code that much.

Manually verified, with manual % escaping of #, using the qrencode CLI encoder alphanumeric mode is indeed used for everything following pj=.

@nothingmuch nothingmuch marked this pull request as draft November 28, 2024 02:20
@coveralls
Copy link
Collaborator

coveralls commented Nov 28, 2024

Pull Request Test Coverage Report for Build 12127285091

Details

  • 177 of 201 (88.06%) changed or added relevant lines in 9 files are covered.
  • 5 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.2%) to 61.786%

Changes Missing Coverage Covered Lines Changed/Added Lines %
payjoin-directory/src/lib.rs 15 18 83.33%
payjoin/src/uri/error.rs 0 4 0.0%
payjoin/src/ohttp.rs 15 20 75.0%
payjoin/src/uri/url_ext.rs 53 58 91.38%
payjoin/src/uri/mod.rs 38 45 84.44%
Files with Coverage Reduction New Missed Lines %
payjoin/src/uri/mod.rs 1 86.97%
payjoin/src/uri/error.rs 1 3.85%
payjoin/src/uri/url_ext.rs 3 91.07%
Totals Coverage Status
Change from base Build 12086002692: 0.2%
Covered Lines: 2857
Relevant Lines: 4624

💛 - Coveralls

@nothingmuch
Copy link
Collaborator Author

Note that ./contrib/lint.sh doesn't pass for all commits, the bech32 module has dead code before later commits are added. I don't see much value in bisectability of linting checks at the sub-merge granularity. ./contrib/test.sh passes for all commits.

@nothingmuch nothingmuch marked this pull request as ready for review November 28, 2024 02:27
@nothingmuch nothingmuch requested a review from DanGould November 28, 2024 02:27
payjoin/src/uri/mod.rs Show resolved Hide resolved
payjoin/src/uri/url_ext.rs Show resolved Hide resolved
payjoin/src/receive/v2/mod.rs Show resolved Hide resolved
payjoin/src/uri/mod.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM, just hoping to clarify some things left in comments

payjoin/src/uri/mod.rs Outdated Show resolved Hide resolved
payjoin/src/uri/mod.rs Show resolved Hide resolved
payjoin/src/uri/url_ext.rs Show resolved Hide resolved
payjoin/src/receive/v2/mod.rs Show resolved Hide resolved
payjoin/src/ohttp.rs Outdated Show resolved Hide resolved
@nothingmuch nothingmuch force-pushed the qr-optimizations branch 2 times, most recently from 77393fc to e8220af Compare December 2, 2024 03:08
@DanGould
Copy link
Contributor

DanGould commented Dec 2, 2024

Ready to merge after one change. bitcoin-0.32.5 exposes bitcoin::bech32 and we should use that.

No checksum or human readable part is included, resulting in 13
uppercase characters, which is amenable to QR code alphanumeric mode.

In the directory, Instead of parsing subdirectory IDs to obtain a byte
array, simply treat them as strings again. This is simpler than
introducing a dependency on the payjoin workspace crate in order to
parse them back into the ShortId type, since this is likely to be a
negligible optimization of the redis serialized key size, and does not
affect the key space.
These changes are to facilitate more efficient QR encoding, by
exploiting the alphanumeric encoding mode.

Use `+` instead of `&` as a parameter delimiter, as only the former is
in the QR alphanumeric character set.

Instead of encoding keys and values using `=` as a delimiter (which also
not in alphanumeric set), the key is the human readable part of a bech32
string with no checksum. This effectively makes the `1` a key/value
delimiter.

The expiry time is serialized as a bitcoin consensus u32 value (little
endian), and also encoded as a bech32 string instead of a decimal value.
Also places the `pj` parameter last.

Following this change and assuming the bip21 # escaping bug fix is
merged, the alphanumeric encoding mode can be used for the entirety of
the (% escaped) pj parameter.
Clearing of the values is not used anywhere, and this simplifies their
type signatures.

PjUriBuilder left unmodified, there is a ticket for it already.
Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 32004db

io = ["reqwest/rustls-tls"]
danger-local-https = ["io", "reqwest/rustls-tls", "rustls"]

[dependencies]
bitcoin = { version = "0.32.4", features = ["base64"] }
bitcoin = { version = "0.32.5", features = ["base64"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This update is missing from payjoin-directory, but I don't think it matters since the specified [^]0.32.4 allows for 0.32.5

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, i missed that. i think this should be addressed in #418

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in fact, cargo udeps points out payjoin-directory doesn't even use its bitcoin dep

@@ -40,7 +42,8 @@ impl std::error::Error for ParseReceiverPubkeyError {

match &self {
MissingPubkey => None,
PubkeyNotBase64(error) => Some(error),
InvalidHrp(_) => None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not pass the source for this one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value is an invalid and untrusted input (but is at least a valid HRP per BIP 173), it doesn't impl std::error::Error so it's not a wrapped error but an error with the offending value included in it

@DanGould DanGould merged commit c54870d into payjoin:master Dec 2, 2024
6 checks passed
DanGould added a commit that referenced this pull request Dec 3, 2024
These commits:

1. encode fragment paramters as bech32 strings (with no checksum), with
  2 character param names in the human readable part, and `+` as a
  delimiter
  - expiry time parameter encoded as a little endian u32, in line with
    bitcoin header timestamp and unix time nLocktime encoding. `EX` hrp
  - ohttp has `OH` hrp, same byte representation as previously encoded in
    base64
  - receiver key has `RK` hrp, same byte representation as previously
    encoded in base64
2. use bech32 with no human readable part for the subdirectory IDs
3. uppercase the scheme and host of the URI
4. move the pj so it follows the pjos parameter

Once the `#` %-escaping bug is fixed (see #373), and as long as no path
elements between the host and the subdirectory ID, the entire `pj`
parameter of the bip21 URI can be encoded in a QR using the alphanumeric
mode. Closes #389.

The last commit seems sufficiently motivated. Since there is no purpose
to clearing these values it doesn't actually simplify the code that much.

Manually verified, with manual % escaping of `#`, using the `qrencode`
CLI encoder alphanumeric mode is indeed used for everything following
`pj=`.
@DanGould DanGould mentioned this pull request Dec 3, 2024
17 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve QR code encoding efficiency
3 participants