Add draft C++ language bindings for payjoin-ffi - #1827
Conversation
Wire a C++ generator into the uniffi-bindgen dispatcher following the same pattern as the C# and Dart generators: an optional cpp feature pulling uniffi-bindgen-cpp, a dispatch arm selected by UNIFFI_BINDGEN_LANGUAGE, and a bindings.cpp stanza in uniffi.toml. Upstream uniffi-bindgen-cpp targets uniffi 0.29 and has no library entry point, so the dependency is pinned to a fork that rebases the in-progress uniffi 0.30 support (NordSecurity/uniffi-bindgen-cpp#59) onto uniffi_bindgen 0.31.2, exposes pub fn main(), adds a --skip-async mode (async is unsupported, their issue payjoin#51), and fixes several generator bugs surfaced by this crate's interface. Both lockfiles are regenerated to include the new dependency tree.
Provide the C++ toolchain (cmake, ninja, the stdenv compiler), libcurl and nlohmann_json for the binding tests, and the pinned msrv rust toolchain, matching the shape of the other per-language binding shells. bitcoind is exposed through BITCOIND_EXE for the integration tests.
Add payjoin-ffi/cpp following the per-language binding layout: scripts/generate_bindings.sh generates C++20 bindings in library mode (skipping the unsupported async exports) and copies the cdylib, CMakeLists.txt builds a consumable payjoin_cpp target plus ctest-registered tests, and contrib/test.sh ties it together against the maintained lockfile. The tests port the python suite: unit tests cover URI parsing, receiver and sender session persistence through an in-memory persister, cancellation, and input validation errors; integration tests drive the full v2-to-v2 payjoin against bitcoind and the payjoin test services with libcurl for OHTTP posts. Register clang-format for the handwritten test sources in treefmt and skip the generated sources in codespell.
Add a cpp workflow mirroring the other language binding workflows and register the cpp binding in the aggregate payjoin-ffi test script.
Coverage Report for CI Build 32370913939Coverage remained the same at 86.718%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
ValeraFinebits
left a comment
There was a problem hiding this comment.
This is a promising draft. I built the bindings and ran the tests under WSL Ubuntu 24.04:
- C++ unit tests: 11/11 passed
- C++ integration tests: 2/2 passed
I found three issues:
- The integration test returns success when no proposal is received after four polls, skipping signing, broadcast, and final assertions. This should fail after a timeout.
--skip-asyncgenerates truncated callback vtables: Rust expects 40 bytes (free/clone/save/load/close), while C++ provides 16 bytes (free/clone). This is an ABI mismatch.BUILD_TESTING=OFFstill requires CURL and nlohmann-json. Test dependencies and targets should be wrapped inif(BUILD_TESTING).
Overall, the synchronous bindings build and run successfully, but I think these issues should be addressed before merging.
|
Here are a few additional recommendations:
The first two affect correctness; the remaining items are non-blocking hardening suggestions for this draft. |
Draft C++ bindings for payjoin-ffi, following the same per-language layout as the C# and Dart bindings: an optional
cppcargo feature and dispatch arm inuniffi-bindgen.rs,payjoin-ffi/cpp/withscripts/generate_bindings.sh+contrib/test.sh, a CMake build exposing a consumablepayjoin_cpptarget, a.#cppnix dev shell, and a CI workflow mirroring the other binding workflows.Generator
There is no uniffi 0.31-compatible C++ generator upstream: NordSecurity/uniffi-bindgen-cpp tops out at v0.9.0+v0.29.4 (metadata contract v29, incompatible with our uniffi 0.31.2 / contract v30), and their uniffi 0.30 migration (NordSecurity/uniffi-bindgen-cpp#59) is unmerged. This PR pins a fork,
chavic/uniffi-bindgen-cpp@830943d, which takes that PR and adds:pub fn main()) mirroring uniffi-bindgen-cs, so it can be consumed as a cargo dep by our dispatcher--skip-asynclibrary mode that drops async exports with a warning instead of failing generation (async is unsupported upstream, #51)shared_ptr, duplicate type definitions from the topological sort, a callback vtable initializer that omitteduniffi_cloneand misordered fields (uniffi 0.30 ABI), a stubbed-out error-deref filter, and even foreign handles (uniffi requires odd ones)The intent is to upstream the fork's commits and re-pin to a NordSecurity release when one supports uniffi 0.31; until then this stays a draft.
Parity caveat
The async API surface (
save_async,replay_*_event_log_async, the*PersisterAsyncinterfaces) is not exposed in C++ — generation skips it. The synchronous API is complete.Testing
nix develop .#cpp -c bash ./payjoin-ffi/cpp/contrib/test.shgenerates the bindings and runs the ported python suite: 11 unit tests (URI parsing, receiver/sender persistence via an in-memory persister subclassing the callback traits, cancel flows, validation errors) and the integration tests (test_ffi_validation,test_integration_v2_to_v2against bitcoind and the payjoin test services). All pass locally on Linux; the macOS leg relies on CI.Disclosure: co-authored by Claude Opus