Skip to content

Insulate bitcoin-ohttp and bitcoin-hpke from the public API - #1702

Merged
spacebear21 merged 2 commits into
payjoin:masterfrom
spacebear21:api/insulate-ohttp-hpke
Jul 8, 2026
Merged

spacebear21 merged 2 commits into
payjoin:masterfrom
spacebear21:api/insulate-ohttp-hpke

Conversation

@spacebear21

Copy link
Copy Markdown
Collaborator

Summary

Part of the 1.0 API hardening: remove pre-1.0 foreign crate types
(bitcoin-ohttp 0.6, bitcoin-hpke 0.13) from payjoin's public API so that a
breaking release of either dependency no longer forces a breaking release of
payjoin. The rust-bitcoin 0.32 and bitcoin_uri 0.1 pins are intentionally kept
for 1.0.

bitcoin-hpke

Make the inner tuple fields of HpkeSecretKey and HpkePublicKey private and
drop their Deref impls, so the wrapped secp256k1/hpke KEM key types are no
longer reachable through the public API. Callers use the existing accessors and
to_/from_compressed_bytes.

bitcoin-ohttp

  • OhttpKeys' inner ohttp::KeyConfig field is now private and the
    Deref/DerefMut impls are removed.
  • OhttpKeys::decode/encode/to_bytes/TryFrom return a new opaque
    OhttpKeysError instead of ohttp::Error (the foreign error is erased
    behind Box<dyn Error + Send + Sync>, keeping the source() chain).
  • The v2 create_*/process_* methods now return/accept a new opaque
    OhttpResponse newtype instead of ohttp::ClientResponse.
  • ohttp_encapsulate takes &OhttpKeys directly.

payjoin-cli and payjoin-ffi are updated to the new API. The FFI's exported
uniffi surface is unchanged (only its internal plumbing types), so the language
bindings do not require regeneration.

The two changes are split into separate commits (hpke, then ohttp) for review.

Disclosure: co-authored by Claude Code (Opus 4.8)

🤖 Generated with Claude Code

HpkeSecretKey and HpkePublicKey each wrapped a bitcoin-hpke KEM key
type behind a public tuple field plus a Deref impl, leaking those
foreign types into payjoin's public API. A breaking release of
bitcoin-hpke would then force a breaking release of payjoin.

Make the inner fields private and drop the Deref impls so the foreign
key types are no longer reachable through the public API. Callers use
the existing accessor methods and to_/from_compressed_bytes, so no
downstream code changes are required.
@coveralls

coveralls commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 28969235109

Coverage increased (+0.04%) to 85.934%

Details

  • Coverage increased (+0.04%) from the base build.
  • Patch coverage: 3 uncovered changes across 1 file (59 of 62 lines covered, 95.16%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
payjoin/src/core/ohttp.rs 21 18 85.71%
Total (7 files) 62 59 95.16%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 15541
Covered Lines: 13355
Line Coverage: 85.93%
Coverage Strength: 350.82 hits per line

💛 - Coveralls

@DanGould

DanGould commented Jul 2, 2026

Copy link
Copy Markdown
Member

concept ACK this is important before 1.0-rc.4

@benalleng

Copy link
Copy Markdown
Collaborator

We either need to remove ohttp as a direct dep in the ffi or add it to the machete ignore
https://github.com/spacebear21/rust-payjoin/blob/09280677f3d766f09ef4035fac730fd152f2d76c/payjoin-ffi/Cargo.toml#L42-L45

2026-07-02T01:09:08.8952465Z error: Cannot build '/nix/store/f0fhx35c3p329xwryaf4904ra9qgr95k-payjoin-workspace-machete-no-version.drv'.
2026-07-02T01:09:08.8956169Z        Reason: builder failed with exit code 1.
2026-07-02T01:09:08.8957125Z        Output paths:
2026-07-02T01:09:08.8958319Z          /nix/store/b27khb70d8d98yn7x80h2lkn3anlw0sx-payjoin-workspace-machete-no-version
2026-07-02T01:09:08.8959514Z        Last 25 log lines:
2026-07-02T01:09:08.8960237Z        > Running phase: configurePhase
2026-07-02T01:09:08.8961935Z        > will append /build/source/.cargo-home/config.toml with contents of /nix/store/hxgfjjmzqzfp75asv5dqibxycc8hc8nd-vendor-cargo-deps/config.toml
2026-07-02T01:09:08.8963478Z        > default configurePhase, nothing to do
2026-07-02T01:09:08.8964249Z        > Running phase: buildPhase
2026-07-02T01:09:08.8964976Z        > +++ command cargo --version
2026-07-02T01:09:08.8965742Z        > cargo 1.98.0-nightly (a335d47ff 2026-06-26)
2026-07-02T01:09:08.8966543Z        > Running phase: checkPhase
2026-07-02T01:09:08.8967257Z        > +++ command cargo machete
2026-07-02T01:09:08.8968049Z        > Analyzing dependencies of crates in this directory...
2026-07-02T01:09:08.8969116Z        > cargo-machete found the following unused dependencies in this directory:
2026-07-02T01:09:08.8970146Z        > payjoin-ffi -- ./payjoin-ffi/Cargo.toml:
2026-07-02T01:09:08.8971287Z        >         ohttp
2026-07-02T01:09:08.8971922Z        >
2026-07-02T01:09:08.8972753Z        > If you believe cargo-machete has detected an unused dependency incorrectly,
2026-07-02T01:09:08.8973933Z        > you can add the dependency to the list of dependencies to ignore in the
2026-07-02T01:09:08.8975427Z        > `[package.metadata.cargo-machete]` section of the appropriate Cargo.toml.
2026-07-02T01:09:08.8976403Z        > For example:
2026-07-02T01:09:08.8976991Z        >
2026-07-02T01:09:08.8977600Z        > [package.metadata.cargo-machete]
2026-07-02T01:09:08.8978346Z        > ignored = ["prost"]
2026-07-02T01:09:08.8978983Z        >
2026-07-02T01:09:08.8980048Z        > You can also try running it with the `--with-metadata` flag for better accuracy,
2026-07-02T01:09:08.8981355Z        > though this may modify your Cargo.lock files.
2026-07-02T01:09:08.8982093Z        >
2026-07-02T01:09:08.8982643Z        > Done!
2026-07-02T01:09:08.8983224Z        For full logs, run:
2026-07-02T01:09:08.8984304Z          nix log /nix/store/f0fhx35c3p329xwryaf4904ra9qgr95k-payjoin-workspace-machete-no-version.drv
2026-07-02T01:09:08.9034590Z error: Cannot build '/nix/store/iszn68brb8jdwnsp2d89b9zsal6q4bfw-maintenance.drv'.
2026-07-02T01:09:08.9035595Z        Reason: 1 dependency failed.
2026-07-02T01:09:08.9036175Z        Output paths:
2026-07-02T01:09:08.9037023Z          /nix/store/yfqyn9ld1y122149ahhzr68b0c8vnrsk-maintenance
2026-07-02T01:09:10.7786183Z ❌ git+file:///home/runner/work/rust-payjoin/rust-payjoin?shallow=1#checks.x86_64-linux.maintenance

Several bitcoin-ohttp types leaked into payjoin's public API: OhttpKeys
exposed its inner ohttp::KeyConfig through a public field and Deref,
decode/to_bytes returned ohttp::Error, and ohttp::ClientResponse
appeared directly in the v2 create_*/process_* signatures. A breaking
release of bitcoin-ohttp would then force a breaking release of payjoin.

Make the OhttpKeys field private and drop its Deref/DerefMut, wrap the
key config error in a new OhttpKeysError that erases ohttp::Error, and
return the OHTTP response context as a new opaque OhttpResponse newtype.
ohttp_encapsulate now takes &OhttpKeys directly.

Tests build OhttpKeys through the public decode API via a new
payjoin_test_utils::ohttp_key_config_bytes helper (plus ohttp_server for
the emulated gateway), since production code only ever constructs
OhttpKeys from directory bytes. payjoin-ffi no longer needs its direct
bitcoin-ohttp dependency, so drop it.
@spacebear21
spacebear21 force-pushed the api/insulate-ohttp-hpke branch from 0928067 to 25ff70d Compare July 8, 2026 19:17
@spacebear21
spacebear21 marked this pull request as ready for review July 8, 2026 19:50
@spacebear21
spacebear21 requested a review from benalleng July 8, 2026 19:50

@benalleng benalleng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

uTACK 25ff70d

I'm satisfied that bitcoin-hpke and bitcoin-ohttp are sufficiently separated so as to no longer be able to break our public api

@spacebear21
spacebear21 merged commit 6e15f1c into payjoin:master Jul 8, 2026
24 of 25 checks passed
@spacebear21 spacebear21 mentioned this pull request Jul 22, 2026
2 tasks
spacebear21 added a commit to spacebear21/rust-payjoin that referenced this pull request Jul 22, 2026
Part of the 1.0 API hardening: remove the foreign bitcoin_uri types
from payjoin's public API so that a breaking release of bitcoin_uri no
longer forces a breaking release of payjoin. This follows the same
approach as the bitcoin-ohttp and bitcoin-hpke insulation in payjoin#1702.

Previously `Uri` and `PjUri` were type aliases to `bitcoin_uri::Uri`,
so the wrapped type, its public fields (including `bitcoin_uri::Param`
labels and messages), the `bitcoin_uri::de::Error` parse error, and the
`bitcoin_uri::Uri` returned by `check_pj_supported` were all reachable
through the public API. The parser traits were also implemented directly
on the public `MaybePayjoinExtras` and `PayjoinExtras` types.

The crate now only names `bitcoin_uri` in private newtype fields and on
the private adapters. No `bitcoin_uri` type or trait impl is reachable
through the public API.
spacebear21 added a commit to spacebear21/rust-payjoin that referenced this pull request Jul 23, 2026
Part of the 1.0 API hardening: remove the foreign bitcoin_uri types
from payjoin's public API so that a breaking release of bitcoin_uri no
longer forces a breaking release of payjoin. This follows the same
approach as the bitcoin-ohttp and bitcoin-hpke insulation in payjoin#1702.

Previously `Uri` and `PjUri` were type aliases to `bitcoin_uri::Uri`,
so the wrapped type, its public fields (including `bitcoin_uri::Param`
labels and messages), the `bitcoin_uri::de::Error` parse error, and the
`bitcoin_uri::Uri` returned by `check_pj_supported` were all reachable
through the public API. The parser traits were also implemented directly
on the public `MaybePayjoinExtras` and `PayjoinExtras` types.

The crate now only names `bitcoin_uri` in private newtype fields and on
the private adapters. No `bitcoin_uri` type or trait impl is reachable
through the public API.
spacebear21 added a commit to spacebear21/rust-payjoin that referenced this pull request Jul 23, 2026
Part of the 1.0 API hardening: remove the foreign bitcoin_uri types
from payjoin's public API so that a breaking release of bitcoin_uri no
longer forces a breaking release of payjoin. This follows the same
approach as the bitcoin-ohttp and bitcoin-hpke insulation in payjoin#1702.

Previously `Uri` and `PjUri` were type aliases to `bitcoin_uri::Uri`,
so the wrapped type, its public fields (including `bitcoin_uri::Param`
labels and messages), the `bitcoin_uri::de::Error` parse error, and the
`bitcoin_uri::Uri` returned by `check_pj_supported` were all reachable
through the public API. The parser traits were also implemented directly
on the public `MaybePayjoinExtras` and `PayjoinExtras` types.

The crate now only names `bitcoin_uri` in private newtype fields and on
the private adapters. No `bitcoin_uri` type or trait impl is reachable
through the public API.
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.

4 participants