Skip to content

feat: port payjoin core to no_std#1615

Open
caarloshenriq wants to merge 5 commits into
payjoin:masterfrom
caarloshenriq:feat/payjoin-nostd
Open

feat: port payjoin core to no_std#1615
caarloshenriq wants to merge 5 commits into
payjoin:masterfrom
caarloshenriq:feat/payjoin-nostd

Conversation

@caarloshenriq
Copy link
Copy Markdown
Contributor

Summary

This is a implementation of no_std support for the payjoin crate,
enabling its use on embedded devices

As discussed in #942, running payjoin logic on a hardware signer enables
stronger security guarantees: the device can verify the fallback transaction,
compare it against the payjoin proposal, and only sign previously-approved
inputs — without trusting the host machine.

Feature Architecture

A new v2-std feature was introduced to separate the state machine logic
from networking dependencies:

Feature Description
alloc bare metal support, state machine logic only
v2 async payjoin session logic without networking
v2-std v2 + networking (url, ohttp, hpke, bhttp, http)
std full std support with tokio, serde_json, bitcoin/base64

Verified Build Targets

  • cargo build -p payjoin --no-default-features --features v2,alloc
  • cargo build -p payjoin --no-default-features --features v2,alloc --target thumbv7em-none-eabihf-p payjoin
  • cargo build -p payjoin --no-default-features --features v2,std

Notes for Reviewers

Please review commit by commit:

refactor: introduce no_std/alloc feature split in payjoin core
The main structural change — replaces std:: with core::/alloc:: and gates std-only deps behind #[cfg(feature = "std")] or #[cfg(feature = "v2-std")].

fix: gate v2 std-only code behind cfg features
Extends gating to v2 send/receive and persist. Key decisions: HasReplyableError now carries fallback_tx in both configs to preserve fallback through replay; MaybeSuccessTransition::deconstruct uses Save instead of SaveAndClose on success.

fix: update payjoin-ffi for no_std feature split
Minimal FFI updates to match new AsyncSessionPersister bounds.

fix: restore OHTTP test constants and enable v2 feature in test utils
KEM, KEY_ID, SYMMETRIC were dropped upstream without updating internal tests. Restores them in payjoin-test-utils/src/v2.rs.

chore: update CI, lock files and flake for no_std targets
Adds thumbv7em-none-eabihf to CI and ARM cross-toolchain to the Nix dev shell.

AI Assistance

This implementation was developed with AI assistance (Claude, Anthropic).

Pull Request Checklist

Please confirm the following before requesting review:

@coveralls
Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 27080196308

Coverage decreased (-0.5%) to 84.823%

Details

  • Coverage decreased (-0.5%) from the base build.
  • Patch coverage: 104 uncovered changes across 14 files (353 of 457 lines covered, 77.24%).
  • 56 coverage regressions across 6 files.

Uncovered Changes

Top 10 Files by Coverage Impact Changed Covered %
payjoin/src/core/send/v2/mod.rs 51 27 52.94%
payjoin/src/core/persist.rs 141 127 90.07%
payjoin/src/core/uri/v2.rs 50 36 72.0%
payjoin/src/core/send/v2/error.rs 11 0 0.0%
payjoin/src/core/receive/v2/mod.rs 19 10 52.63%
payjoin/src/core/send/error.rs 22 14 63.64%
payjoin/src/core/ohttp.rs 7 1 14.29%
payjoin/src/core/uri/mod.rs 105 100 95.24%
payjoin/src/core/error.rs 6 2 33.33%
payjoin/src/core/time.rs 5 1 20.0%
Total (22 files) 457 353 77.24%

Coverage Regressions

56 previously-covered lines in 6 files lost coverage.

File Lines Losing Coverage Coverage
payjoin/src/core/persist.rs 31 92.5%
payjoin/src/core/receive/v2/mod.rs 16 88.5%
payjoin/src/core/receive/v2/error.rs 4 38.18%
payjoin/src/core/send/v2/error.rs 2 30.23%
payjoin/src/core/uri/error.rs 2 4.55%
payjoin/src/core/send/error.rs 1 42.79%

Coverage Stats

Coverage Status
Relevant Lines: 14720
Covered Lines: 12486
Line Coverage: 84.82%
Coverage Strength: 371.16 hits per line

💛 - Coveralls

@DanGould
Copy link
Copy Markdown
Contributor

DanGould commented Jun 7, 2026

Great to see this take off here. My biggest question is about v2-std which expresses that it's about "networking" but really it's the wire serialization. Is it possible to use the library without that? I'm not sure it is.

Copy link
Copy Markdown
Collaborator

@benalleng benalleng left a comment

Choose a reason for hiding this comment

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

Looks like a good start, though there are some feature organizations I have some questions about.

Comment thread payjoin/Cargo.toml
Copy link
Copy Markdown
Collaborator

@benalleng benalleng Jun 7, 2026

Choose a reason for hiding this comment

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

Keep in mind that the test-utils dep needs to sanitize its v2 feature from payjoin so that the tests run properly as according to ./payjoin/contrib/test.sh namely v2 should gate the payjoin-test-utils/v2 feature and it should not always be pulled in

Comment on lines +35 to +37
"io",
"_manual-tls",
"_test-utils",
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.

Could you explain why this was necessary?

Comment thread payjoin/Cargo.toml
tracing = { version = "0.1.41", default-features = false, features = [
"attributes",
] }
url = { version = "2.5.4", optional = true, default-features = false }
Copy link
Copy Markdown
Collaborator

@benalleng benalleng Jun 7, 2026

Choose a reason for hiding this comment

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

We put a lot of effort into removing the url as anything but a transitive dep outside of the io feature, was this deemed unavoidable?

Comment thread flake.nix
Comment on lines +291 to +299
# secp256k1-sys build.rs invokes cc-rs for the wasm32-unknown-unknown
# target; cc-rs defaults to clang for wasm and needs llvm-ar.
llvmPackages.clang-unwrapped
llvmPackages.bintools-unwrapped
lld
# Version must match the wasm-bindgen crate locked in
# payjoin-ffi/javascript/rust_modules/wasm/Cargo.lock.
wasm-bindgen-cli_0_2_108
gcc-arm-embedded
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.

Keep in mind we now have the ability to run nix develop .#javascript so as to not need these across all shells.

Comment thread flake.nix
Comment on lines +306 to +310
DOTNET_ROOT = "${dotnetSdk}/share/dotnet";
DOTNET_CLI_TELEMETRY_OPTOUT = "1";
CC_wasm32_unknown_unknown = "${pkgs.llvmPackages.clang-unwrapped}/bin/clang";
AR_wasm32_unknown_unknown = "${pkgs.llvmPackages.bintools-unwrapped}/bin/llvm-ar";
CC_thumbv7em_none_eabihf = "arm-none-eabi-gcc";
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.

Same here they seem language specific.

Comment thread payjoin/Cargo.toml
]
directory = []
v1 = ["_core"]
v2 = ["_core", "hpke", "bhttp", "ohttp", "directory", "payjoin-test-utils/v2"]
Copy link
Copy Markdown
Collaborator

@benalleng benalleng Jun 7, 2026

Choose a reason for hiding this comment

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

Ideally we want to keep the payjoin-test-utils/v2 locked behind the v2 feature or _test-utils +v2

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