From 4157a3babacca1a63fac12364446d79aa464b378 Mon Sep 17 00:00:00 2001 From: Cyber-Lord Date: Sun, 31 Aug 2025 16:50:07 +0100 Subject: [PATCH 1/4] Format monorepo with nix fmt through treefmt-nix Run: dart format nixfmt prettier ruff format rustfmt shellcheck shfmt taplo format --- .cargo/mutants.toml | 57 +- .github/CONTRIBUTING.md | 9 +- .github/ISSUE_TEMPLATE/minor-release.md | 9 +- .github/pull_request_template.md | 4 +- .github/workflows/cron-directory-monitor.yml | 2 +- .github/workflows/cron-weekly-mutants.yml | 4 +- .github/workflows/dart.yml | 1 - .github/workflows/rust.yml | 7 +- Cargo.toml | 9 +- ohttp-relay/CHANGELOG.md | 5 +- ohttp-relay/Cargo.toml | 26 +- ohttp-relay/README.md | 2 +- payjoin-cli/CHANGELOG.md | 2 +- payjoin-cli/Cargo.toml | 19 +- payjoin-cli/README.md | 2 + payjoin-directory/Cargo.toml | 22 +- payjoin-ffi/CHANGELOG.md | 76 +- payjoin-ffi/Cargo.toml | 9 +- payjoin-ffi/README.md | 10 +- payjoin-ffi/dart/README.md | 1 - payjoin-ffi/dart/native/Cargo.toml | 4 +- payjoin-ffi/dart/pubspec.yaml | 2 +- .../test/test_payjoin_integration_test.dart | 249 ++-- .../dart/test/test_payjoin_unit_test.dart | 54 +- payjoin-ffi/javascript/README.md | 1 - payjoin-ffi/javascript/package-lock.json | 1194 ++++++++--------- payjoin-ffi/javascript/package.json | 74 +- payjoin-ffi/javascript/test-utils/build.rs | 4 +- .../javascript/test-utils/package-lock.json | 58 +- .../javascript/test-utils/package.json | 26 +- payjoin-ffi/javascript/test/unit.test.ts | 42 +- payjoin-ffi/javascript/tsconfig.json | 36 +- payjoin-ffi/javascript/ubrn.config.yaml | 12 +- payjoin-ffi/python/CHANGELOG.md | 70 +- payjoin-ffi/python/pyproject.toml | 21 +- payjoin-ffi/python/setup.py | 4 +- .../test/test_payjoin_integration_test.py | 258 +++- .../python/test/test_payjoin_unit_test.py | 52 +- payjoin-test-utils/CHANGELOG.md | 2 +- payjoin-test-utils/Cargo.toml | 14 +- payjoin/CHANGELOG.md | 51 +- payjoin/Cargo.toml | 30 +- taplo.toml | 13 + 43 files changed, 1446 insertions(+), 1101 deletions(-) create mode 100644 taplo.toml diff --git a/.cargo/mutants.toml b/.cargo/mutants.toml index e67abb4a5..e04d98af2 100644 --- a/.cargo/mutants.toml +++ b/.cargo/mutants.toml @@ -1,37 +1,34 @@ additional_cargo_args = ["--all-features"] gitignore = true -examine_globs = [ - "payjoin/src/**/*.rs" -] -exclude_globs = [ -] +examine_globs = ["payjoin/src/**/*.rs"] +exclude_globs = [] exclude_re = [ - "impl\\s+(std::fmt::|core::fmt::|fmt::)?(Display|Debug)", - "deserialize", - "Iterator", - ".*Error", + "impl\\s+(std::fmt::|core::fmt::|fmt::)?(Display|Debug)", + "deserialize", + "Iterator", + ".*Error", - # ---------------------Crate-specific exclusions --------------------- - # Timeout loops - # src/receive/v1/mod.rs - "interleave_shuffle", # Replacing index += 1 with index *= 1 in a loop causes a timeout due to an infinite loop + # ---------------------Crate-specific exclusions --------------------- + # Timeout loops + # src/receive/v1/mod.rs + "interleave_shuffle", # Replacing index += 1 with index *= 1 in a loop causes a timeout due to an infinite loop - # Trivial mutations - # These exclusions are allowing code blocks to run with arithmetic involving zero and as a result are no-ops - # payjoin/src/core/send/mod.rs - "replace < with <= in PsbtContext::check_outputs", - "replace > with >= in PsbtContext::check_fees", - # payjoin/src/core/send/mod.rs - "replace < with <= in PsbtContextBuilder::build_recommended", # clamping the fee contribution when the fee equals to the recommended fee does not do anything - # payjoin/src/core/receive/v2/mod.rs - "payjoin/src/core/receive/v2.mod.*replace \\* with [+/]$", # This targets mutations on the static TWENTY_FOUR_HOURS_DEFAULT_EXPIRATION that is not particularly useful to test for + # Trivial mutations + # These exclusions are allowing code blocks to run with arithmetic involving zero and as a result are no-ops + # payjoin/src/core/send/mod.rs + "replace < with <= in PsbtContext::check_outputs", + "replace > with >= in PsbtContext::check_fees", + # payjoin/src/core/send/mod.rs + "replace < with <= in PsbtContextBuilder::build_recommended", # clamping the fee contribution when the fee equals to the recommended fee does not do anything + # payjoin/src/core/receive/v2/mod.rs + "payjoin/src/core/receive/v2.mod.*replace \\* with [+/]$", # This targets mutations on the static TWENTY_FOUR_HOURS_DEFAULT_EXPIRATION that is not particularly useful to test for - # TODO exclusions - # payjoin/src/core/receive/v1/mod.rs - "replace > with >= in WantsInputs::avoid_uih", # This mutation I am unsure about whether or not it is a trivial mutant and have not decided on how the best way to approach testing it is - # payjoin/src/core/send/mod.rs - "replace match guard proposed_txout.script_pubkey == original_output.script_pubkey with true in PsbtContext::check_outputs", # This non-deterministic mutation has a possible test to catch it - # These will be removed following #1123 - # payjoin/src/core/send/v2/session.rs - "SessionHistory::terminal_error", + # TODO exclusions + # payjoin/src/core/receive/v1/mod.rs + "replace > with >= in WantsInputs::avoid_uih", # This mutation I am unsure about whether or not it is a trivial mutant and have not decided on how the best way to approach testing it is + # payjoin/src/core/send/mod.rs + "replace match guard proposed_txout.script_pubkey == original_output.script_pubkey with true in PsbtContext::check_outputs", # This non-deterministic mutation has a possible test to catch it + # These will be removed following #1123 + # payjoin/src/core/send/v2/session.rs + "SessionHistory::terminal_error", ] diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 077eaa230..77a341ec7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -85,13 +85,14 @@ As a small exception, trivial tab-completion doesn't need to be disclosed, so long as it is limited to single keywords or short phrases. An example disclosure: + > This PR was written primarily by Claude Code. -Or a more detailed disclosure: +> Or a more detailed disclosure: > I consulted ChatGPT to understand the codebase but the solution > was fully authored manually by myself. -Failure to disclose this is impolite to the human operators -on the other end of the pull request, and it also makes it difficult to -determine how much scrutiny to apply to the contribution. +> Failure to disclose this is impolite to the human operators +> on the other end of the pull request, and it also makes it difficult to +> determine how much scrutiny to apply to the contribution. Please be respectful to maintainers and disclose AI assistance so that they may help you effectively contribute. diff --git a/.github/ISSUE_TEMPLATE/minor-release.md b/.github/ISSUE_TEMPLATE/minor-release.md index 21e8fb149..0f4b36946 100644 --- a/.github/ISSUE_TEMPLATE/minor-release.md +++ b/.github/ISSUE_TEMPLATE/minor-release.md @@ -2,9 +2,8 @@ name: Minor Release about: Checklist for releasing a new minor version bump title: Release MAJOR.MINOR+1.0 -labels: '' -assignees: '' - +labels: "" +assignees: "" --- ## Create a new minor release @@ -24,7 +23,7 @@ assignees: '' ### Checklist Release numbering must follow [Semantic Versioning]. These steps assume the current `master` -branch **development** version is *MAJOR.MINOR.0*. +branch **development** version is _MAJOR.MINOR.0_. #### On the day of the feature freeze @@ -41,7 +40,7 @@ Change the `master` branch to the next MINOR+1 version: - Title PR "Bump CRATE version to MAJOR.MINOR+1.0". - [ ] Merge the `bump-CRATE-MAJOR-MINOR+1` branch to `master`. -If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released: +If any issues need to be fixed before the _MAJOR.MINOR+1.0_ version is released: - [ ] Merge fix PRs to the `master` branch. - [ ] Git cherry-pick fix commits to the `bump-CRATE-MAJOR.MINOR+1` branch. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 645ad90e1..871418632 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ Please confirm the following before requesting review: - [ ] I have [disclosed my use of - AI](https://github.com/payjoin/rust-payjoin/blob/master/.github/CONTRIBUTING.md#ai-assistance-notice) - in the body of this PR. + AI](https://github.com/payjoin/rust-payjoin/blob/master/.github/CONTRIBUTING.md#ai-assistance-notice) + in the body of this PR. - [ ] I have read [CONTRIBUTING.md](https://github.com/payjoin/rust-payjoin/blob/master/.github/CONTRIBUTING.md#commits) and **rebased my branch to produce [hygienic commits](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md#committing-patches)**. diff --git a/.github/workflows/cron-directory-monitor.yml b/.github/workflows/cron-directory-monitor.yml index 9e273fe49..345c932c8 100644 --- a/.github/workflows/cron-directory-monitor.yml +++ b/.github/workflows/cron-directory-monitor.yml @@ -1,4 +1,4 @@ -name : Payjoin directory health check +name: Payjoin directory health check on: schedule: - cron: "*/15 * * * *" # Every 15 minutes diff --git a/.github/workflows/cron-weekly-mutants.yml b/.github/workflows/cron-weekly-mutants.yml index d18feefd9..129f368b1 100644 --- a/.github/workflows/cron-weekly-mutants.yml +++ b/.github/workflows/cron-weekly-mutants.yml @@ -1,6 +1,6 @@ name: Weekly cargo-mutants -on: - schedule: +on: + schedule: - cron: "0 0 * * 0" # runs weekly on Sunday at 00:00 workflow_dispatch: # allows manual triggering jobs: diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index f569cee21..dbd90610d 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -40,4 +40,3 @@ jobs: - name: Run tests run: dart test - diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 721e57030..1c0f6429a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -131,15 +131,15 @@ jobs: run: bash contrib/coverage.sh - name: "Upload report to coveralls" uses: coverallsapp/github-action@v2 - - CodeSpell: + + CodeSpell: name: Code spell check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: codespell-project/actions-codespell@v2 - DiffMutants: + DiffMutants: name: Diff cargo-mutants runs-on: ubuntu-latest env: @@ -172,4 +172,3 @@ jobs: with: name: mutants-incremental-cargo.out path: mutants.out - diff --git a/Cargo.toml b/Cargo.toml index 67e22f9f9..0e16ea6c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,12 @@ [workspace] -members = ["ohttp-relay", "payjoin", "payjoin-cli", "payjoin-directory", "payjoin-test-utils", "payjoin-ffi"] +members = [ + "ohttp-relay", + "payjoin", + "payjoin-cli", + "payjoin-directory", + "payjoin-test-utils", + "payjoin-ffi", +] resolver = "2" [patch.crates-io] diff --git a/ohttp-relay/CHANGELOG.md b/ohttp-relay/CHANGELOG.md index 551e44f81..3918787e6 100644 --- a/ohttp-relay/CHANGELOG.md +++ b/ohttp-relay/CHANGELOG.md @@ -41,9 +41,10 @@ requests to Gateways that do not support an allowed purpose. - Gateway opt-in was introduced in [#58](https://github.com/payjoin/ohttp-relay/pull/58) ### Gateway Probing and BIP77 Support + - Added gateway probing functionality with caching mechanism for improved performance [#46](https://github.com/payjoin/ohttp-relay/pull/46) -Implemented BIP77 purpose string detection in allowed purposes response #47 -Added ALPN-encoded format parsing for gateway allowed purposes #50 + Implemented BIP77 purpose string detection in allowed purposes response #47 + Added ALPN-encoded format parsing for gateway allowed purposes #50 - https://github.com/payjoin/ohttp-relay/pull/46 - https://github.com/payjoin/ohttp-relay/pull/47 diff --git a/ohttp-relay/Cargo.toml b/ohttp-relay/Cargo.toml index 31140b476..06e50b0b5 100644 --- a/ohttp-relay/Cargo.toml +++ b/ohttp-relay/Cargo.toml @@ -26,11 +26,22 @@ futures = { version = "0.3.31", optional = true } http = "1.3.1" http-body-util = "0.1.3" hyper = { version = "1.6.0", features = ["http1", "server"] } -hyper-rustls = { version = "0.27.7", default-features=false, features = ["webpki-roots", "http1", "ring"] } +hyper-rustls = { version = "0.27.7", default-features = false, features = [ + "webpki-roots", + "http1", + "ring", +] } hyper-tungstenite = { version = "0.18.0", optional = true } hyper-util = { version = "0.1.16", features = ["client-legacy"] } -rustls = { version = "0.23.31", optional = true, default-features=false, features = ["ring"] } -tokio = { version = "1.47.1", features = ["io-std", "macros", "net", "rt-multi-thread"] } +rustls = { version = "0.23.31", optional = true, default-features = false, features = [ + "ring", +] } +tokio = { version = "1.47.1", features = [ + "io-std", + "macros", + "net", + "rt-multi-thread", +] } tokio-tungstenite = { version = "0.27.0", optional = true } tokio-util = { version = "0.7.16", features = ["net", "codec"] } tracing = "0.1.41" @@ -40,8 +51,13 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } hex = { package = "hex-conservative", version = "0.1.1" } mockito = "1.7.0" rcgen = "0.12" +reqwest = { version = "0.12.23", default-features = false, features = [ + "rustls-tls", + "blocking", +] } tempfile = "3.20.0" tokio = { version = "1.47.1", features = ["process", "test-util"] } -tokio-rustls = { version = "0.26.2", default-features=false, features = ["ring"]} -reqwest = { version = "0.12.23", default-features= false ,features = ["rustls-tls", "blocking"] } +tokio-rustls = { version = "0.26.2", default-features = false, features = [ + "ring", +] } uuid = { version = "1.18.0", features = ["v4"] } diff --git a/ohttp-relay/README.md b/ohttp-relay/README.md index 79e073446..2f6e89ed6 100644 --- a/ohttp-relay/README.md +++ b/ohttp-relay/README.md @@ -4,7 +4,7 @@ A rust implementation of an [Oblivious HTTP](https://ietf-wg-ohai.github.io/oblivious-http/draft-ietf-ohai-ohttp.html) relay resource. This work is undergoing active revision in the IETF and so are these -implementations. Use at your own risk. +implementations. Use at your own risk. ## Usage diff --git a/payjoin-cli/CHANGELOG.md b/payjoin-cli/CHANGELOG.md index 89cb0d13c..6a985e2eb 100644 --- a/payjoin-cli/CHANGELOG.md +++ b/payjoin-cli/CHANGELOG.md @@ -19,7 +19,7 @@ - Allow mixed input scripts [#367](https://github.com/payjoin/rust-payjoin/pull/367) [#505](https://github.com/payjoin/rust-payjoin/pull/505) - Fix bug to propagate missing config parameter or argument error [#441](https://github.com/payjoin/rust-payjoin/pull/441) - Don't pause between long polling requests [#463](https://github.com/payjoin/rust-payjoin/pull/463) -- Hide danger-local-https feature with _ prefix [#423](https://github.com/payjoin/rust-payjoin/pull/423) +- Hide danger-local-https feature with \_ prefix [#423](https://github.com/payjoin/rust-payjoin/pull/423) - Allow specifying a max-feerate for receivers [#332](https://github.com/payjoin/rust-payjoin/pull/332) - Fix e2e tests and coverage reporting [#443](https://github.com/payjoin/rust-payjoin/pull/443) [#497](https://github.com/payjoin/rust-payjoin/pull/497) [#532](https://github.com/payjoin/rust-payjoin/pull/532) - Handle recoverable receiver errors by replying to sender with error response [#474](https://github.com/payjoin/rust-payjoin/pull/474) [#526](https://github.com/payjoin/rust-payjoin/pull/526) [#534](https://github.com/payjoin/rust-payjoin/pull/534) diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index b5487afb1..4d70c9791 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -22,31 +22,36 @@ path = "src/main.rs" default = ["v2"] native-certs = ["reqwest/rustls-tls-native-roots"] _manual-tls = ["reqwest/rustls-tls", "payjoin/_manual-tls", "tokio-rustls"] -v1 = ["payjoin/v1","hyper", "hyper-util", "http-body-util"] +v1 = ["payjoin/v1", "hyper", "hyper-util", "http-body-util"] v2 = ["payjoin/v2", "payjoin/io"] [dependencies] anyhow = "1.0.99" async-trait = "0.1.89" -bitcoind-async-client = {git = "https://github.com/arminsabouri/bitcoind-async-client", rev = "956ca693e4263c003eaa4fa938d909d24acac5fa"} +bitcoind-async-client = { git = "https://github.com/arminsabouri/bitcoind-async-client", rev = "956ca693e4263c003eaa4fa938d909d24acac5fa" } clap = { version = "4.5.45", features = ["derive"] } config = "0.15.14" +dirs = "6.0.0" http-body-util = { version = "0.1.3", optional = true } hyper = { version = "1.6.0", features = ["http1", "server"], optional = true } hyper-util = { version = "0.1.16", optional = true } payjoin = { version = "1.0.0-rc.1", default-features = false } r2d2 = "0.8.10" r2d2_sqlite = "0.22.0" -reqwest = { version = "0.12.23", default-features = false, features = ["json", "rustls-tls"] } +reqwest = { version = "0.12.23", default-features = false, features = [ + "json", + "rustls-tls", +] } rusqlite = { version = "0.29.0", features = ["bundled"] } -serde_json = "1.0.142" serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.142" tokio = { version = "1.47.1", features = ["full"] } -tokio-rustls = { version = "0.26.2", features = ["ring"], default-features = false, optional = true } -url = { version = "2.5.4", features = ["serde"] } -dirs = "6.0.0" +tokio-rustls = { version = "0.26.2", features = [ + "ring", +], default-features = false, optional = true } tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } +url = { version = "2.5.4", features = ["serde"] } [dev-dependencies] nix = { version = "0.30.1", features = ["aio", "process", "signal"] } diff --git a/payjoin-cli/README.md b/payjoin-cli/README.md index 566a12043..da2e86cc1 100644 --- a/payjoin-cli/README.md +++ b/payjoin-cli/README.md @@ -65,6 +65,7 @@ touch sender/config.toml receiver/config.toml Edit the `config.toml` files. #### `sender/config.toml` + ```toml # Nigiri uses the following RPC credentials [bitcoind] @@ -79,6 +80,7 @@ ohttp_relays = ["https://pj.benalleng.com", "https://pj.bobspacebkk.com", "https ``` #### `receiver/config.toml` + ```toml # Nigiri uses the following RPC credentials [bitcoind] diff --git a/payjoin-directory/Cargo.toml b/payjoin-directory/Cargo.toml index f08300755..35c8704fc 100644 --- a/payjoin-directory/Cargo.toml +++ b/payjoin-directory/Cargo.toml @@ -20,25 +20,29 @@ acme = ["tokio-rustls-acme"] [dependencies] anyhow = "1.0.99" -bitcoin = { version = "0.32.7", features = ["base64", "rand-std"] } bhttp = { version = "0.6.1", features = ["http"] } +bitcoin = { version = "0.32.7", features = ["base64", "rand-std"] } +clap = { version = "4.5.45", features = ["derive", "env"] } +config = "0.15.14" futures = "0.3.31" http-body-util = "0.1.3" hyper = { version = "1.6.0", features = ["http1", "server"] } hyper-util = { version = "0.1.16", features = ["tokio"] } -ohttp = { package = "bitcoin-ohttp", version = "0.6.0"} -payjoin = { version = "1.0.0-rc.1", features = ["directory"], default-features = false } +ohttp = { package = "bitcoin-ohttp", version = "0.6.0" } +payjoin = { version = "1.0.0-rc.1", features = [ + "directory", +], default-features = false } +prometheus = "0.13.4" +rand = "0.8" +serde = { version = "1.0.219", features = ["derive"] } tokio = { version = "1.47.1", features = ["full"] } -tokio-rustls = { version = "0.26.2", features = ["ring"], default-features = false, optional = true } +tokio-rustls = { version = "0.26.2", features = [ + "ring", +], default-features = false, optional = true } tokio-rustls-acme = { version = "0.7.1", optional = true } tokio-stream = { version = "0.1.17", features = ["net"] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -prometheus = "0.13.4" -clap = { version = "4.5.45", features = ["derive", "env"] } -config = "0.15.14" -serde = { version = "1.0.219", features = ["derive"] } -rand = "0.8" [dev-dependencies] tempfile = "3.20.0" diff --git a/payjoin-ffi/CHANGELOG.md b/payjoin-ffi/CHANGELOG.md index eb8b1d27a..e1843b209 100644 --- a/payjoin-ffi/CHANGELOG.md +++ b/payjoin-ffi/CHANGELOG.md @@ -1,4 +1,3 @@ - ## [0.24.0] ### Session Persister @@ -35,76 +34,97 @@ - Rename to payjoin-ffi from payjoin_ffi to match bitcoin-ffi ## [0.22.1] + - Expose label and message params on Uri. ([#44](https://github.com/LtbLightning/payjoin-ffi/pull/44)) ## [0.22.0] + - Update `payjoin` to `0.22.0`. (Serialize reply_key with Sender [#41](https://github.com/LtbLightning/payjoin-ffi/pull/41)) ## [0.21.2] + - Add `pj_endpoint` method to `PjUri` types. ([#40](https://github.com/LtbLightning/payjoin-ffi/pull/40)) ## [0.21.1] + - Add `to_json` and `from_json` methods to `Sender` and `Receiver` UniFFI types. ([#39](https://github.com/LtbLightning/payjoin-ffi/pull/39)) ## [0.21.0] + This release updates the bindings libraries to `payjoin` version `0.21.0`. + #### APIs changed + - Major overhaul to attempt a stable BIP 77 protocol implementation. - v1 support is now only available through the V2 backwards-compatible APIs. - see [payjoin-0.21.0 changelog](https://github.com/payjoin/rust-payjoin/blob/master/payjoin/CHANGELOG.md#0210) for more details. - Separate `payjoin_ffi` and `payjoin_ffi::uni` UniFFI types into two layers. ## [0.20.0] + #### APIs added + - Make backwards-compatible `v2` to `v1` sends possible. + #### APIs changed -- Removed `contribute_non_nitness_input` from `v1` & `v2`. + +- Removed `contribute_non_nitness_input` from `v1` & `v2`. - Allow receivers to make `payjoins` out of sweep transactions ([#259](https://github.com/payjoin/rust-payjoin/pull/259)). - Encode &ohttp= and &exp= parameters in the &pj= URL as a fragment instead of as URI params ([#298](https://github.com/payjoin/rust-payjoin/pull/298)) ## [0.18.0] + This release updates the bindings libraries to `payjoin` version `0.18.0`. + #### APIs changed + - Upgrade `receive/v2` type state machine to resume multiple `payjoins` simultaneously ([#283](https://github.com/payjoin/rust-payjoin/pull/283)) - Refactor output substitution with new fallable `try_substitute_outputs` ([#277](https://github.com/payjoin/rust-payjoin/pull/277)) - Replaced `Enroller` with `SessionInitializer`. - Replaced `Enrolled` with `ActiveSession`. - Replaced `fallback_target()` with `pj_url`. + #### APIs added + - Exposed `PjUriBuilder` and `PjUri`. - Exposed `pjUrl_builder()` in `ActiveSession`. - Exposed `check_pj_supported()` in `PjUri`. - Exposed `fetch_ohttp_keys()` to fetch the `ohttp` keys from the specified `payjoin` directory. ## [0.13.0] + ### Features & Modules + #### Send module -- ##### V1 - - `RequestBuilder` exposes `from_psbt_and_uri`, `build_with_additional_fee`, `build_recommended`, `build_non_incentivizing`, `always_disable_output_substitution`. - - `RequestContext` exposes `extract_contextV1` & `extract_contextV2`. - - `ContextV1` exposes `process_response`. + +- ##### V1 + - `RequestBuilder` exposes `from_psbt_and_uri`, `build_with_additional_fee`, `build_recommended`, `build_non_incentivizing`, `always_disable_output_substitution`. + - `RequestContext` exposes `extract_contextV1` & `extract_contextV2`. + - `ContextV1` exposes `process_response`. - ##### V2 - - `ContextV2` exposes `process_response`. + - `ContextV2` exposes `process_response`. + #### Receive module -- ##### V1 - - `UncheckedProposal` exposes `from_request`, `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability`, `build_non_incentivizing`, - `assume_interactive_receiver` &`always_disable_output_substitution`. - - `MaybeInputsOwned` exposes `check_inputs_not_owned`. - - `MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. - - `MaybeInputsSeen` exposes `check_no_inputs_seen_before`. - - `OutputsUnknown` exposes `identify_receiver_outputs`. - - `ProvisionalProposal` exposes `substitute_output_address`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & - `finalize_proposal`. - - `PayjoinProposal` exposes `is_output_substitution_disabled`, `owned_vouts`, `psbt` & `utxos_to_be_locked`. + +- ##### V1 + - `UncheckedProposal` exposes `from_request`, `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability`, `build_non_incentivizing`, + `assume_interactive_receiver` &`always_disable_output_substitution`. + - `MaybeInputsOwned` exposes `check_inputs_not_owned`. + - `MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. + - `MaybeInputsSeen` exposes `check_no_inputs_seen_before`. + - `OutputsUnknown` exposes `identify_receiver_outputs`. + - `ProvisionalProposal` exposes `substitute_output_address`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & + `finalize_proposal`. + - `PayjoinProposal` exposes `is_output_substitution_disabled`, `owned_vouts`, `psbt` & `utxos_to_be_locked`. - ##### V2 - - `Enroller` exposes `from_directory_config`, `process_response` & `extract_request`. - - `Enrolled` exposes `extract_request`, `process_response` & `fall_back_target`. - - `V2UncheckedProposal` exposes `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability` & `assume_interactive_receiver`. - - `V2MaybeInputsOwned` exposes `check_inputs_not_owned`. - - `V2MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. - - `V2MaybeInputsSeen` exposes `check_no_inputs_seen_before`. - - `V2OutputsUnknown` exposes `identify_receiver_outputs`. - - `V2ProvisionalProposal` exposes `substitute_output_address`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & - `finalize_proposal`. - - `V2PayjoinProposal` exposes `deserialize_res`, `extract_v1_req`, `extract_v2_req`, `is_output_substitution_disabled`, `owned_vouts`, `psbt` & - `utxos_to_be_locked`. + - `Enroller` exposes `from_directory_config`, `process_response` & `extract_request`. + - `Enrolled` exposes `extract_request`, `process_response` & `fall_back_target`. + - `V2UncheckedProposal` exposes `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability` & `assume_interactive_receiver`. + - `V2MaybeInputsOwned` exposes `check_inputs_not_owned`. + - `V2MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. + - `V2MaybeInputsSeen` exposes `check_no_inputs_seen_before`. + - `V2OutputsUnknown` exposes `identify_receiver_outputs`. + - `V2ProvisionalProposal` exposes `substitute_output_address`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & + `finalize_proposal`. + - `V2PayjoinProposal` exposes `deserialize_res`, `extract_v1_req`, `extract_v2_req`, `is_output_substitution_disabled`, `owned_vouts`, `psbt` & + `utxos_to_be_locked`. diff --git a/payjoin-ffi/Cargo.toml b/payjoin-ffi/Cargo.toml index 1e48a1db8..b1458f2f8 100644 --- a/payjoin-ffi/Cargo.toml +++ b/payjoin-ffi/Cargo.toml @@ -32,8 +32,8 @@ serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.142" thiserror = "2.0.14" tokio = { version = "1.47.1", features = ["full"], optional = true } -uniffi-dart = { git = "https://github.com/Uniffi-Dart/uniffi-dart.git", rev = "5bdcc79", optional = true } uniffi = { version = "0.30.0" } +uniffi-dart = { git = "https://github.com/Uniffi-Dart/uniffi-dart.git", rev = "5bdcc79", optional = true } url = "2.5.4" # getrandom is ignored here because it's required by the wasm_js feature @@ -42,4 +42,9 @@ url = "2.5.4" ignored = ["getrandom"] [dev-dependencies] -bdk = { version = "0.29.0", features = ["all-keys", "use-esplora-ureq", "keys-bip39", "rpc"] } +bdk = { version = "0.29.0", features = [ + "all-keys", + "use-esplora-ureq", + "keys-bip39", + "rpc", +] } diff --git a/payjoin-ffi/README.md b/payjoin-ffi/README.md index 93e0aaaff..34f81e971 100644 --- a/payjoin-ffi/README.md +++ b/payjoin-ffi/README.md @@ -10,11 +10,11 @@ Each supported language is in its own directory. The Rust code in this project i The directories below include instructions for using, building, and publishing the native language bindings for [Payjoin Dev Kit] supported by this project. -| Language | Platform | Repository | Published Package | -|----------|-----------------------|-------------------|------------------------------------| -| Python | linux, macOS | [payjoin-ffi/python](python) | [payjoin](https://pypi.org/project/payjoin/) | -| Dart | linux, macOS | [payjoin-ffi/dart](dart) | N/A | -| JavaScript | linux, macOS | [payjoin-ffi/javascript](javascript) | N/A | +| Language | Platform | Repository | Published Package | +| ---------- | ------------ | ------------------------------------ | -------------------------------------------- | +| Python | linux, macOS | [payjoin-ffi/python](python) | [payjoin](https://pypi.org/project/payjoin/) | +| Dart | linux, macOS | [payjoin-ffi/dart](dart) | N/A | +| JavaScript | linux, macOS | [payjoin-ffi/javascript](javascript) | N/A | ## Minimum Supported Rust Version (MSRV) diff --git a/payjoin-ffi/dart/README.md b/payjoin-ffi/dart/README.md index 6fa63c37b..000c837b6 100644 --- a/payjoin-ffi/dart/README.md +++ b/payjoin-ffi/dart/README.md @@ -6,7 +6,6 @@ Welcome to the Dart language bindings for the [Payjoin Dev Kit](https://payjoind Follow these steps to clone the repository and run the tests. - ```shell git clone https://github.com/payjoin/rust-payjoin.git cd rust-payjoin/payjoin-ffi/dart diff --git a/payjoin-ffi/dart/native/Cargo.toml b/payjoin-ffi/dart/native/Cargo.toml index 4b120831d..206b21716 100644 --- a/payjoin-ffi/dart/native/Cargo.toml +++ b/payjoin-ffi/dart/native/Cargo.toml @@ -16,7 +16,9 @@ name = "payjoin_ffi_wrapper" crate-type = ["staticlib", "cdylib"] [dependencies] -payjoin-ffi = { git = "https://github.com/payjoin/rust-payjoin.git", branch = "master", features = ["dart"] } +payjoin-ffi = { git = "https://github.com/payjoin/rust-payjoin.git", branch = "master", features = [ + "dart", +] } [patch."https://github.com/payjoin/rust-payjoin.git"] payjoin-ffi = { path = "../.." } diff --git a/payjoin-ffi/dart/pubspec.yaml b/payjoin-ffi/dart/pubspec.yaml index e10eed84c..4995a8fd4 100644 --- a/payjoin-ffi/dart/pubspec.yaml +++ b/payjoin-ffi/dart/pubspec.yaml @@ -3,7 +3,7 @@ description: Dart bindings for payjoin (EXPERIMENTAL) version: 0.1.0 environment: - sdk: '^3.10.0' + sdk: "^3.10.0" dependencies: ffi: ^2.1.4 diff --git a/payjoin-ffi/dart/test/test_payjoin_integration_test.dart b/payjoin-ffi/dart/test/test_payjoin_integration_test.dart index 406574c37..b84fd8b02 100644 --- a/payjoin-ffi/dart/test/test_payjoin_integration_test.dart +++ b/payjoin-ffi/dart/test/test_payjoin_integration_test.dart @@ -92,8 +92,10 @@ class IsScriptOwnedCallback implements payjoin.IsScriptOwned { bool callback(Uint8List script) { try { final scriptObj = bitcoin.Script(script); - final address = - bitcoin.Address.fromScript(scriptObj, bitcoin.Network.regtest); + final address = bitcoin.Address.fromScript( + scriptObj, + bitcoin.Network.regtest, + ); // This is a hack due to toString() not being exposed by dart FFI final address_str = address.toQrUri().split(":")[1]; final result = connection.call("getaddressinfo", [address_str]); @@ -130,41 +132,58 @@ class ProcessPsbtCallback implements payjoin.ProcessPsbt { } payjoin.Initialized create_receiver_context( - bitcoin.Address address, - String directory, - payjoin.OhttpKeys ohttp_keys, - InMemoryReceiverPersister persister) { - var receiver = payjoin.ReceiverBuilder(address, directory, ohttp_keys) - .build() - .save(persister); + bitcoin.Address address, + String directory, + payjoin.OhttpKeys ohttp_keys, + InMemoryReceiverPersister persister, +) { + var receiver = payjoin.ReceiverBuilder( + address, + directory, + ohttp_keys, + ).build().save(persister); return receiver; } String build_sweep_psbt(payjoin.RpcClient sender, payjoin.PjUri pj_uri) { var outputs = {}; outputs[pj_uri.address()] = 50; - var psbt = jsonDecode(sender.call("walletcreatefundedpsbt", [ - jsonEncode([]), - jsonEncode(outputs), - jsonEncode(0), - jsonEncode({ - "lockUnspents": true, - "fee_rate": 10, - "subtractFeeFromOutputs": [0] - }) - ]))["psbt"]; - return jsonDecode(sender.call("walletprocesspsbt", - [psbt, jsonEncode(true), jsonEncode("ALL"), jsonEncode(false)]))["psbt"]; + var psbt = jsonDecode( + sender.call("walletcreatefundedpsbt", [ + jsonEncode([]), + jsonEncode(outputs), + jsonEncode(0), + jsonEncode({ + "lockUnspents": true, + "fee_rate": 10, + "subtractFeeFromOutputs": [0], + }), + ]), + )["psbt"]; + return jsonDecode( + sender.call("walletprocesspsbt", [ + psbt, + jsonEncode(true), + jsonEncode("ALL"), + jsonEncode(false), + ]), + )["psbt"]; } List get_inputs(payjoin.RpcClient rpc_connection) { var utxos = jsonDecode(rpc_connection.call("listunspent", [])); List inputs = []; for (var utxo in utxos) { - var txin = bitcoin.TxIn(bitcoin.OutPoint(utxo["txid"], utxo["vout"]), - bitcoin.Script(Uint8List.fromList([])), 0, []); - var tx_out = bitcoin.TxOut(bitcoin.Amount.fromBtc(utxo["amount"]), - bitcoin.Script(Uint8List.fromList(hex.decode(utxo["scriptPubKey"])))); + var txin = bitcoin.TxIn( + bitcoin.OutPoint(utxo["txid"], utxo["vout"]), + bitcoin.Script(Uint8List.fromList([])), + 0, + [], + ); + var tx_out = bitcoin.TxOut( + bitcoin.Amount.fromBtc(utxo["amount"]), + bitcoin.Script(Uint8List.fromList(hex.decode(utxo["scriptPubKey"]))), + ); var psbt_in = payjoin.PsbtInput(tx_out, null, null); inputs.add(payjoin.InputPair(txin, psbt_in, null)); } @@ -173,8 +192,9 @@ List get_inputs(payjoin.RpcClient rpc_connection) { } Future process_provisional_proposal( - payjoin.ProvisionalProposal proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.ProvisionalProposal proposal, + InMemoryReceiverPersister recv_persister, +) async { final payjoin_proposal = proposal .finalizeProposal(ProcessPsbtCallback(receiver)) .save(recv_persister); @@ -182,15 +202,17 @@ Future process_provisional_proposal( } Future process_wants_fee_range( - payjoin.WantsFeeRange proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.WantsFeeRange proposal, + InMemoryReceiverPersister recv_persister, +) async { final wants_fee_range = proposal.applyFeeRange(1, 10).save(recv_persister); return await process_provisional_proposal(wants_fee_range, recv_persister); } Future process_wants_inputs( - payjoin.WantsInputs proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.WantsInputs proposal, + InMemoryReceiverPersister recv_persister, +) async { final provisional_proposal = proposal .contributeInputs(get_inputs(receiver)) .commitInputs() @@ -199,15 +221,17 @@ Future process_wants_inputs( } Future process_wants_outputs( - payjoin.WantsOutputs proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.WantsOutputs proposal, + InMemoryReceiverPersister recv_persister, +) async { final wants_inputs = proposal.commitOutputs().save(recv_persister); return await process_wants_inputs(wants_inputs, recv_persister); } Future process_outputs_unknown( - payjoin.OutputsUnknown proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.OutputsUnknown proposal, + InMemoryReceiverPersister recv_persister, +) async { final wants_outputs = proposal .identifyReceiverOutputs(IsScriptOwnedCallback(receiver)) .save(recv_persister); @@ -215,8 +239,9 @@ Future process_outputs_unknown( } Future process_maybe_inputs_seen( - payjoin.MaybeInputsSeen proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.MaybeInputsSeen proposal, + InMemoryReceiverPersister recv_persister, +) async { final outputs_unknown = proposal .checkNoInputsSeenBefore(CheckInputsNotSeenCallback(receiver)) .save(recv_persister); @@ -224,8 +249,9 @@ Future process_maybe_inputs_seen( } Future process_maybe_inputs_owned( - payjoin.MaybeInputsOwned proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.MaybeInputsOwned proposal, + InMemoryReceiverPersister recv_persister, +) async { final maybe_inputs_owned = proposal .checkInputsNotOwned(IsScriptOwnedCallback(receiver)) .save(recv_persister); @@ -233,8 +259,9 @@ Future process_maybe_inputs_owned( } Future process_unchecked_proposal( - payjoin.UncheckedOriginalPayload proposal, - InMemoryReceiverPersister recv_persister) async { + payjoin.UncheckedOriginalPayload proposal, + InMemoryReceiverPersister recv_persister, +) async { final unchecked_proposal = proposal .checkBroadcastSuitability(null, MempoolAcceptanceCallback(receiver)) .save(recv_persister); @@ -242,14 +269,17 @@ Future process_unchecked_proposal( } Future retrieve_receiver_proposal( - payjoin.Initialized receiver, - InMemoryReceiverPersister recv_persister, - String ohttp_relay) async { + payjoin.Initialized receiver, + InMemoryReceiverPersister recv_persister, + String ohttp_relay, +) async { var agent = http.Client(); var request = receiver.createPollRequest(ohttp_relay); - var response = await agent.post(Uri.parse(request.request.url), - headers: {"Content-Type": request.request.contentType}, - body: request.request.body); + var response = await agent.post( + Uri.parse(request.request.url), + headers: {"Content-Type": request.request.contentType}, + body: request.request.body, + ); var res = receiver .processResponse(response.bodyBytes, request.clientResponse) .save(recv_persister); @@ -265,12 +295,16 @@ Future retrieve_receiver_proposal( } Future process_receiver_proposal( - payjoin.ReceiveSession receiver, - InMemoryReceiverPersister recv_persister, - String ohttp_relay) async { + payjoin.ReceiveSession receiver, + InMemoryReceiverPersister recv_persister, + String ohttp_relay, +) async { if (receiver is payjoin.InitializedReceiveSession) { var res = await retrieve_receiver_proposal( - receiver.inner, recv_persister, ohttp_relay); + receiver.inner, + recv_persister, + ohttp_relay, + ); if (res == null) { return null; } @@ -313,8 +347,9 @@ void main() { receiver = env.getReceiver(); sender = env.getSender(); var receiver_address = bitcoin.Address( - jsonDecode(receiver.call("getnewaddress", [])), - bitcoin.Network.regtest); + jsonDecode(receiver.call("getnewaddress", [])), + bitcoin.Network.regtest, + ); var services = payjoin.TestServices.initialize(); services.waitForServicesReady(); @@ -328,11 +363,16 @@ void main() { var recv_persister = InMemoryReceiverPersister("1"); var sender_persister = InMemorySenderPersister("1"); var session = create_receiver_context( - receiver_address, directory, ohttp_keys, recv_persister); + receiver_address, + directory, + ohttp_keys, + recv_persister, + ); var process_response = await process_receiver_proposal( - payjoin.InitializedReceiveSession(session), - recv_persister, - ohttp_relay); + payjoin.InitializedReceiveSession(session), + recv_persister, + ohttp_relay, + ); expect(process_response, isNull); // ********************** @@ -340,14 +380,18 @@ void main() { // Create a funded PSBT (not broadcasted) to address with amount given in the pj_uri var pj_uri = session.pjUri(); var psbt = build_sweep_psbt(sender, pj_uri); - payjoin.WithReplyKey req_ctx = payjoin.SenderBuilder(psbt, pj_uri) - .buildRecommended(1000) - .save(sender_persister); - payjoin.RequestOhttpContext request = - req_ctx.createV2PostRequest(ohttp_relay); - var response = await agent.post(Uri.parse(request.request.url), - headers: {"Content-Type": request.request.contentType}, - body: request.request.body); + payjoin.WithReplyKey req_ctx = payjoin.SenderBuilder( + psbt, + pj_uri, + ).buildRecommended(1000).save(sender_persister); + payjoin.RequestOhttpContext request = req_ctx.createV2PostRequest( + ohttp_relay, + ); + var response = await agent.post( + Uri.parse(request.request.url), + headers: {"Content-Type": request.request.contentType}, + body: request.request.body, + ); payjoin.PollingForProposal send_ctx = req_ctx .processResponse(response.bodyBytes, request.ohttpCtx) .save(sender_persister); @@ -359,59 +403,76 @@ void main() { // GET fallback psbt payjoin.ReceiveSession? payjoin_proposal = await process_receiver_proposal( - payjoin.InitializedReceiveSession(session), - recv_persister, - ohttp_relay); + payjoin.InitializedReceiveSession(session), + recv_persister, + ohttp_relay, + ); expect(payjoin_proposal, isNotNull); expect(payjoin_proposal, isA()); payjoin.PayjoinProposal proposal = (payjoin_proposal as payjoin.PayjoinProposalReceiveSession).inner; - payjoin.RequestResponse request_response = - proposal.createPostRequest(ohttp_relay); + payjoin.RequestResponse request_response = proposal.createPostRequest( + ohttp_relay, + ); var fallback_response = await agent.post( - Uri.parse(request_response.request.url), - headers: {"Content-Type": request_response.request.contentType}, - body: request_response.request.body); + Uri.parse(request_response.request.url), + headers: {"Content-Type": request_response.request.contentType}, + body: request_response.request.body, + ); proposal.processResponse( - fallback_response.bodyBytes, request_response.clientResponse); + fallback_response.bodyBytes, + request_response.clientResponse, + ); // ********************** // Inside the Sender: // Sender checks, isngs, finalizes, extracts, and broadcasts // Replay post fallback to get the response - payjoin.RequestOhttpContext ohttp_context_request = - send_ctx.createPollRequest(ohttp_relay); + payjoin.RequestOhttpContext ohttp_context_request = send_ctx + .createPollRequest(ohttp_relay); var final_response = await agent.post( - Uri.parse(ohttp_context_request.request.url), - headers: {"Content-Type": ohttp_context_request.request.contentType}, - body: ohttp_context_request.request.body); + Uri.parse(ohttp_context_request.request.url), + headers: {"Content-Type": ohttp_context_request.request.contentType}, + body: ohttp_context_request.request.body, + ); var checked_payjoin_proposal_psbt = send_ctx .processResponse( - final_response.bodyBytes, ohttp_context_request.ohttpCtx) + final_response.bodyBytes, + ohttp_context_request.ohttpCtx, + ) .save(sender_persister); expect(checked_payjoin_proposal_psbt, isNotNull); - var checked_payjoin_proposal_psbt_inner = (checked_payjoin_proposal_psbt - as payjoin.ProgressPollingForProposalTransitionOutcome) - .inner; - var payjoin_psbt = jsonDecode(sender.call("walletprocesspsbt", - [checked_payjoin_proposal_psbt_inner.serializeBase64()]))["psbt"]; - var final_psbt = jsonDecode(sender - .call("finalizepsbt", [payjoin_psbt, jsonEncode(false)]))["psbt"]; + var checked_payjoin_proposal_psbt_inner = + (checked_payjoin_proposal_psbt + as payjoin.ProgressPollingForProposalTransitionOutcome) + .inner; + var payjoin_psbt = jsonDecode( + sender.call("walletprocesspsbt", [ + checked_payjoin_proposal_psbt_inner.serializeBase64(), + ]), + )["psbt"]; + var final_psbt = jsonDecode( + sender.call("finalizepsbt", [payjoin_psbt, jsonEncode(false)]), + )["psbt"]; var payjoin_tx = bitcoin.Psbt.deserializeBase64(final_psbt).extractTx(); - sender.call("sendrawtransaction", - [jsonEncode(hex.encode(payjoin_tx.serialize()))]); + sender.call("sendrawtransaction", [ + jsonEncode(hex.encode(payjoin_tx.serialize())), + ]); // Check resulting transaction and balances - var network_fees = - bitcoin.Psbt.deserializeBase64(final_psbt).fee().toBtc(); + var network_fees = bitcoin.Psbt.deserializeBase64( + final_psbt, + ).fee().toBtc(); // Sender sent the entire value of their utxo to the receiver (minus fees) expect(payjoin_tx.input().length, 2); expect(payjoin_tx.output().length, 1); expect( - jsonDecode(receiver.call("getbalances", []))["mine"] - ["untrusted_pending"], - 100 - network_fees); + jsonDecode( + receiver.call("getbalances", []), + )["mine"]["untrusted_pending"], + 100 - network_fees, + ); expect(jsonDecode(sender.call("getbalance", [])), 0.0); }); }); diff --git a/payjoin-ffi/dart/test/test_payjoin_unit_test.dart b/payjoin-ffi/dart/test/test_payjoin_unit_test.dart index 1a80431eb..2ec1e557e 100644 --- a/payjoin-ffi/dart/test/test_payjoin_unit_test.dart +++ b/payjoin-ffi/dart/test/test_payjoin_unit_test.dart @@ -57,8 +57,11 @@ void main() { var uri = "bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?amount=1&pj=https://example.com?ciao"; final result = payjoin.Url.parse(uri); - expect(result, isA(), - reason: "pj url should be url encoded"); + expect( + result, + isA(), + reason: "pj url should be url encoded", + ); }); test('Test valid url', () { @@ -104,38 +107,59 @@ void main() { test("Test receiver persistence", () { var persister = InMemoryReceiverPersister("1"); var address = bitcoin.Address( - "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", bitcoin.Network.signet); + "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", + bitcoin.Network.signet, + ); payjoin.ReceiverBuilder( address, "https://example.com", - payjoin.OhttpKeys.decode(Uint8List.fromList(hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003"))), + payjoin.OhttpKeys.decode( + Uint8List.fromList( + hex.decode( + "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", + ), + ), + ), ).build().save(persister); final result = payjoin.replayReceiverEventLog(persister); - expect(result, isA(), - reason: "persistence should return a replay result"); + expect( + result, + isA(), + reason: "persistence should return a replay result", + ); }); test("Test sender persistence", () { var receiver_persister = InMemoryReceiverPersister("1"); var address = bitcoin.Address( - "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", bitcoin.Network.testnet); + "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", + bitcoin.Network.testnet, + ); var receiver = payjoin.ReceiverBuilder( address, "https://example.com", - payjoin.OhttpKeys.decode(Uint8List.fromList(hex.decode( - "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003"))), + payjoin.OhttpKeys.decode( + Uint8List.fromList( + hex.decode( + "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003", + ), + ), + ), ).build().save(receiver_persister); var uri = receiver.pjUri(); var sender_persister = InMemorySenderPersister("1"); var psbt = "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA="; - final result = payjoin.SenderBuilder(psbt, uri) - .buildRecommended(1000) - .save(sender_persister); - expect(result, isA(), - reason: "persistence should return a reply key"); + final result = payjoin.SenderBuilder( + psbt, + uri, + ).buildRecommended(1000).save(sender_persister); + expect( + result, + isA(), + reason: "persistence should return a reply key", + ); }); }); } diff --git a/payjoin-ffi/javascript/README.md b/payjoin-ffi/javascript/README.md index 0a19082a6..69a3bdf42 100644 --- a/payjoin-ffi/javascript/README.md +++ b/payjoin-ffi/javascript/README.md @@ -7,7 +7,6 @@ Welcome to the JavaScript language bindings for the [Payjoin Dev Kit](https://pa Follow these steps to clone the repository and run the tests. This assumes you already have Rust and Node.js installed. - ```shell git clone https://github.com/payjoin/rust-payjoin.git cd rust-payjoin/payjoin-ffi/javascript diff --git a/payjoin-ffi/javascript/package-lock.json b/payjoin-ffi/javascript/package-lock.json index 5f8ac4ca8..8c287bac5 100644 --- a/payjoin-ffi/javascript/package-lock.json +++ b/payjoin-ffi/javascript/package-lock.json @@ -1,600 +1,600 @@ { - "name": "payjoin", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "payjoin", - "version": "0.1.0", - "devDependencies": { - "prettier": "^3.6.2", - "tsx": "^4.20.6", - "typescript": "^5.9.3", - "uniffi-bindgen-react-native": "github:spacebear21/uniffi-bindgen-react-native#update-uniffi-0.30-wasm" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uniffi-bindgen-react-native": { - "version": "0.29.3-1", - "resolved": "git+ssh://git@github.com/spacebear21/uniffi-bindgen-react-native.git#723c0c77c45db1eb74e832a8814e4ac36d07fc28", - "dev": true, - "license": "MPL-2.0", - "bin": { - "ubrn": "bin/cli.cjs", - "uniffi-bindgen-react-native": "bin/cli.cjs" - } + "name": "payjoin", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "payjoin", + "version": "0.1.0", + "devDependencies": { + "prettier": "^3.6.2", + "tsx": "^4.20.6", + "typescript": "^5.9.3", + "uniffi-bindgen-react-native": "github:spacebear21/uniffi-bindgen-react-native#update-uniffi-0.30-wasm" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uniffi-bindgen-react-native": { + "version": "0.29.3-1", + "resolved": "git+ssh://git@github.com/spacebear21/uniffi-bindgen-react-native.git#723c0c77c45db1eb74e832a8814e4ac36d07fc28", + "dev": true, + "license": "MPL-2.0", + "bin": { + "ubrn": "bin/cli.cjs", + "uniffi-bindgen-react-native": "bin/cli.cjs" + } + } } - } } diff --git a/payjoin-ffi/javascript/package.json b/payjoin-ffi/javascript/package.json index 78560dbc6..5b515cc49 100644 --- a/payjoin-ffi/javascript/package.json +++ b/payjoin-ffi/javascript/package.json @@ -1,39 +1,39 @@ { - "name": "payjoin", - "version": "0.1.0", - "description": "JavaScript/WASM bindings for rust-payjoin", - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "browser": "dist/index.web.js", - "scripts": { - "ubrn:web": "ubrn build web --and-generate", - "compile": "tsc", - "copy-wasm": "mkdir -p dist/generated && cp -r src/generated/wasm-bindgen dist/generated/", - "fix-imports": "node scripts/fix-imports.js", - "build": "npm run ubrn:web && npm run compile && npm run copy-wasm && npm run fix-imports", - "build:test-utils": "cd test-utils && npm install && npx @napi-rs/cli build", - "clean": "rm -rf rust_modules dist src/generated", - "test": "tsx --test test/unit.test.ts test/integration.test.ts" - }, - "files": [ - "dist/**/*" - ], - "keywords": [ - "payjoin", - "bitcoin", - "wasm", - "rust" - ], - "devDependencies": { - "prettier": "^3.6.2", - "tsx": "^4.20.6", - "typescript": "^5.9.3", - "uniffi-bindgen-react-native": "github:spacebear21/uniffi-bindgen-react-native#update-uniffi-0.30-wasm" - }, - "repository": { - "type": "git", - "url": "https://github.com/payjoin/rust-payjoin.git", - "directory": "payjoin-ffi/javascript" - } + "name": "payjoin", + "version": "0.1.0", + "description": "JavaScript/WASM bindings for rust-payjoin", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "browser": "dist/index.web.js", + "scripts": { + "ubrn:web": "ubrn build web --and-generate", + "compile": "tsc", + "copy-wasm": "mkdir -p dist/generated && cp -r src/generated/wasm-bindgen dist/generated/", + "fix-imports": "node scripts/fix-imports.js", + "build": "npm run ubrn:web && npm run compile && npm run copy-wasm && npm run fix-imports", + "build:test-utils": "cd test-utils && npm install && npx @napi-rs/cli build", + "clean": "rm -rf rust_modules dist src/generated", + "test": "tsx --test test/unit.test.ts test/integration.test.ts" + }, + "files": [ + "dist/**/*" + ], + "keywords": [ + "payjoin", + "bitcoin", + "wasm", + "rust" + ], + "devDependencies": { + "prettier": "^3.6.2", + "tsx": "^4.20.6", + "typescript": "^5.9.3", + "uniffi-bindgen-react-native": "github:spacebear21/uniffi-bindgen-react-native#update-uniffi-0.30-wasm" + }, + "repository": { + "type": "git", + "url": "https://github.com/payjoin/rust-payjoin.git", + "directory": "payjoin-ffi/javascript" + } } diff --git a/payjoin-ffi/javascript/test-utils/build.rs b/payjoin-ffi/javascript/test-utils/build.rs index 0f1b01002..89c974efa 100644 --- a/payjoin-ffi/javascript/test-utils/build.rs +++ b/payjoin-ffi/javascript/test-utils/build.rs @@ -1,3 +1 @@ -fn main() { - napi_build::setup(); -} +fn main() { napi_build::setup(); } diff --git a/payjoin-ffi/javascript/test-utils/package-lock.json b/payjoin-ffi/javascript/test-utils/package-lock.json index 48f803515..d6f312aac 100644 --- a/payjoin-ffi/javascript/test-utils/package-lock.json +++ b/payjoin-ffi/javascript/test-utils/package-lock.json @@ -1,32 +1,32 @@ { - "name": "payjoin-test-utils", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "payjoin-test-utils", - "version": "0.1.0", - "devDependencies": { - "@napi-rs/cli": "^2.18.0" - } - }, - "node_modules/@napi-rs/cli": { - "version": "2.18.4", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", - "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", - "dev": true, - "license": "MIT", - "bin": { - "napi": "scripts/index.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } + "name": "payjoin-test-utils", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "payjoin-test-utils", + "version": "0.1.0", + "devDependencies": { + "@napi-rs/cli": "^2.18.0" + } + }, + "node_modules/@napi-rs/cli": { + "version": "2.18.4", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", + "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", + "dev": true, + "license": "MIT", + "bin": { + "napi": "scripts/index.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + } } - } } diff --git a/payjoin-ffi/javascript/test-utils/package.json b/payjoin-ffi/javascript/test-utils/package.json index 46337d3d5..269dfdaad 100644 --- a/payjoin-ffi/javascript/test-utils/package.json +++ b/payjoin-ffi/javascript/test-utils/package.json @@ -1,15 +1,15 @@ { - "name": "payjoin-test-utils", - "version": "0.1.0", - "description": "Native Node.js bindings for payjoin test utilities", - "type": "module", - "main": "index.js", - "types": "index.d.ts", - "napi": { - "name": "payjoin-test-utils-napi", - "triples": {} - }, - "devDependencies": { - "@napi-rs/cli": "^2.18.0" - } + "name": "payjoin-test-utils", + "version": "0.1.0", + "description": "Native Node.js bindings for payjoin test utilities", + "type": "module", + "main": "index.js", + "types": "index.d.ts", + "napi": { + "name": "payjoin-test-utils-napi", + "triples": {} + }, + "devDependencies": { + "@napi-rs/cli": "^2.18.0" + } } diff --git a/payjoin-ffi/javascript/test/unit.test.ts b/payjoin-ffi/javascript/test/unit.test.ts index 32a9595b1..8259581bb 100644 --- a/payjoin-ffi/javascript/test/unit.test.ts +++ b/payjoin-ffi/javascript/test/unit.test.ts @@ -82,8 +82,10 @@ describe("URI tests", () => { "http://vjdpwgybvubne5hda6v4c5iaeeevhge6jvo3w2cl6eocbwwvwxp7b7qd.onion"; const base58 = "bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX"; - const bech32Upper = "BITCOIN:TB1Q6D3A2W975YNY0ASUVD9A67NER4NKS58FF0Q8G4"; - const bech32Lower = "bitcoin:tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; + const bech32Upper = + "BITCOIN:TB1Q6D3A2W975YNY0ASUVD9A67NER4NKS58FF0Q8G4"; + const bech32Lower = + "bitcoin:tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4"; const addresses = [base58, bech32Upper, bech32Lower]; const pjs = [https, onion]; @@ -109,13 +111,14 @@ describe("Persistence tests", () => { ); const ohttpKeys = payjoin.OhttpKeys.decode( new Uint8Array([ - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, 0x92, - 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, 0x0c, 0xed, - 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, 0xad, 0x2c, 0xc9, - 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, 0xe8, 0xd2, 0x8b, 0xfe, - 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, 0x4d, 0x31, 0xcd, 0x85, 0x1e, - 0x8a, 0x65, 0x40, 0xe8, 0x6c, 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, - 0x09, 0x57, 0x00, 0x04, 0x00, 0x01, 0x00, 0x03, + 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, + 0x92, 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, + 0x0c, 0xed, 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, + 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, + 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, + 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, + 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, + 0x00, 0x01, 0x00, 0x03, ]).buffer, ); @@ -129,7 +132,11 @@ describe("Persistence tests", () => { const result = payjoin.replayReceiverEventLog(persister); const state = result.state(); - assert.strictEqual(state.tag, "Initialized", "State should be Initialized"); + assert.strictEqual( + state.tag, + "Initialized", + "State should be Initialized", + ); }); test("sender persistence", () => { @@ -140,13 +147,14 @@ describe("Persistence tests", () => { ); const ohttpKeys = payjoin.OhttpKeys.decode( new Uint8Array([ - 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, 0x92, - 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, 0x0c, 0xed, - 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, 0xad, 0x2c, 0xc9, - 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, 0xe8, 0xd2, 0x8b, 0xfe, - 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, 0x4d, 0x31, 0xcd, 0x85, 0x1e, - 0x8a, 0x65, 0x40, 0xe8, 0x6c, 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, - 0x09, 0x57, 0x00, 0x04, 0x00, 0x01, 0x00, 0x03, + 0x01, 0x00, 0x16, 0x04, 0xba, 0x48, 0xc4, 0x9c, 0x3d, 0x4a, + 0x92, 0xa3, 0xad, 0x00, 0xec, 0xc6, 0x3a, 0x02, 0x4d, 0xa1, + 0x0c, 0xed, 0x02, 0x18, 0x0c, 0x73, 0xec, 0x12, 0xd8, 0xa7, + 0xad, 0x2c, 0xc9, 0x1b, 0xb4, 0x83, 0x82, 0x4f, 0xe2, 0xbe, + 0xe8, 0xd2, 0x8b, 0xfe, 0x2e, 0xb2, 0xfc, 0x64, 0x53, 0xbc, + 0x4d, 0x31, 0xcd, 0x85, 0x1e, 0x8a, 0x65, 0x40, 0xe8, 0x6c, + 0x53, 0x82, 0xaf, 0x58, 0x8d, 0x37, 0x09, 0x57, 0x00, 0x04, + 0x00, 0x01, 0x00, 0x03, ]).buffer, ); diff --git a/payjoin-ffi/javascript/tsconfig.json b/payjoin-ffi/javascript/tsconfig.json index 3826359fb..fd2c15ef3 100644 --- a/payjoin-ffi/javascript/tsconfig.json +++ b/payjoin-ffi/javascript/tsconfig.json @@ -1,20 +1,20 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "lib": ["es2022", "dom"], - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "moduleResolution": "node", - "resolveJsonModule": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "rust_modules"] + "compilerOptions": { + "target": "es2022", + "module": "es2022", + "lib": ["es2022", "dom"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "rust_modules"] } diff --git a/payjoin-ffi/javascript/ubrn.config.yaml b/payjoin-ffi/javascript/ubrn.config.yaml index 88b8a4b19..0d1c9c389 100644 --- a/payjoin-ffi/javascript/ubrn.config.yaml +++ b/payjoin-ffi/javascript/ubrn.config.yaml @@ -1,9 +1,9 @@ rust: - directory: .. - manifestPath: Cargo.toml + directory: .. + manifestPath: Cargo.toml web: - features: ["wasm_js"] - defaultFeatures: false - target: nodejs - manifestPatchFile: wasm-manifest-patch.toml + features: ["wasm_js"] + defaultFeatures: false + target: nodejs + manifestPatchFile: wasm-manifest-patch.toml diff --git a/payjoin-ffi/python/CHANGELOG.md b/payjoin-ffi/python/CHANGELOG.md index 03e2ba2ad..b9da03abd 100644 --- a/payjoin-ffi/python/CHANGELOG.md +++ b/payjoin-ffi/python/CHANGELOG.md @@ -1,44 +1,56 @@ ## [0.20.0] + #### APIs added + - Make backwards-compatible `v2` to `v1` sends possible. + #### APIs changed -- Removed `contribute_non_nitness_input` from `v1` & `v2`. + +- Removed `contribute_non_nitness_input` from `v1` & `v2`. - Allow receivers to make `payjoins` out of sweep transactions ([#259](https://github.com/payjoin/rust-payjoin/pull/259)). - Encode &ohttp= and &exp= parameters in the &pj= URL as a fragment instead of as URI params ([#298](https://github.com/payjoin/rust-payjoin/pull/298)) ## [0.18.0] + This release updates the python library to `payjoin` version `0.18.0`. + ### Features & Modules + #### Send module -- ##### V1 - - `RequestBuilder` exposes `from_psbt_and_uri`, `build_with_additional_fee`, `build_recommended`, `build_non_incentivizing`, - `always_disable_output_substitution`. - - `RequestContext` exposes `extract_contextV1` & `extract_contextV2`. - - `ContextV1` exposes `process_response`. + +- ##### V1 + - `RequestBuilder` exposes `from_psbt_and_uri`, `build_with_additional_fee`, `build_recommended`, `build_non_incentivizing`, + `always_disable_output_substitution`. + - `RequestContext` exposes `extract_contextV1` & `extract_contextV2`. + - `ContextV1` exposes `process_response`. - ##### V2 - - `ContextV2` exposes `process_response`. + - `ContextV2` exposes `process_response`. + #### Receive module -- ##### V1 - - `UncheckedProposal` exposes `from_request`, `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability`, `build_non_incentivizing`, - `assume_interactive_receiver` & `always_disable_output_substitution`. - - `MaybeInputsOwned` exposes `check_inputs_not_owned`. - - `MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. - - `MaybeInputsSeen` exposes `check_no_inputs_seen_before`. - - `OutputsUnknown` exposes `identify_receiver_outputs`. - - `ProvisionalProposal` exposes `try_substitute_receiver_output`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & - `finalize_proposal`. - - `PayjoinProposal` exposes `is_output_substitution_disabled`, `owned_vouts`, `psbt` & `utxos_to_be_locked`. + +- ##### V1 + - `UncheckedProposal` exposes `from_request`, `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability`, `build_non_incentivizing`, + `assume_interactive_receiver` & `always_disable_output_substitution`. + - `MaybeInputsOwned` exposes `check_inputs_not_owned`. + - `MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. + - `MaybeInputsSeen` exposes `check_no_inputs_seen_before`. + - `OutputsUnknown` exposes `identify_receiver_outputs`. + - `ProvisionalProposal` exposes `try_substitute_receiver_output`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & + `finalize_proposal`. + - `PayjoinProposal` exposes `is_output_substitution_disabled`, `owned_vouts`, `psbt` & `utxos_to_be_locked`. - ##### V2 - - `SessionInitializer` exposes `from_directory_config`, `process_res` & `extract_request`. - - `ActiveSession` exposes `extract_request`, `process_res`, `pj_uri_builder` & `pj_url`. - - `V2UncheckedProposal` exposes `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability` & `assume_interactive_receiver`. - - `V2MaybeInputsOwned` exposes `check_inputs_not_owned`. - - `V2MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. - - `V2MaybeInputsSeen` exposes `check_no_inputs_seen_before`. - - `V2OutputsUnknown` exposes `identify_receiver_outputs`. - - `V2ProvisionalProposal` exposes `try_substitute_receiver_output`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & - `finalize_proposal`. - - `V2PayjoinProposal` exposes `process_res`, `extract_v1_req`, `extract_v2_req`, `is_output_substitution_disabled`, `owned_vouts`, `psbt` & - `utxos_to_be_locked`. + - `SessionInitializer` exposes `from_directory_config`, `process_res` & `extract_request`. + - `ActiveSession` exposes `extract_request`, `process_res`, `pj_uri_builder` & `pj_url`. + - `V2UncheckedProposal` exposes `extract_tx_to_schedule_broadcast`, `check_broadcast_suitability` & `assume_interactive_receiver`. + - `V2MaybeInputsOwned` exposes `check_inputs_not_owned`. + - `V2MaybeMixedInputScripts` exposes `check_no_mixed_input_scripts`. + - `V2MaybeInputsSeen` exposes `check_no_inputs_seen_before`. + - `V2OutputsUnknown` exposes `identify_receiver_outputs`. + - `V2ProvisionalProposal` exposes `try_substitute_receiver_output`, `contribute_non_witness_input`, `contribute_witness_input`, `try_preserving_privacy` & + `finalize_proposal`. + - `V2PayjoinProposal` exposes `process_res`, `extract_v1_req`, `extract_v2_req`, `is_output_substitution_disabled`, `owned_vouts`, `psbt` & + `utxos_to_be_locked`. + #### io module -- Exposed `fetch_ohttp_keys()` to fetch the `ohttp` keys from the specified `payjoin` directory. \ No newline at end of file + +- Exposed `fetch_ohttp_keys()` to fetch the `ohttp` keys from the specified `payjoin` directory. diff --git a/payjoin-ffi/python/pyproject.toml b/payjoin-ffi/python/pyproject.toml index 1a25a865b..191ec8aae 100644 --- a/payjoin-ffi/python/pyproject.toml +++ b/payjoin-ffi/python/pyproject.toml @@ -10,12 +10,12 @@ requires-python = ">=3.9" license = "MIT" dynamic = ["version"] dependencies = [ - "build==1.3.0", - "semantic-version==2.9.0", - "setuptools==78.1.1", - "typing-extensions==4.0.1", - "wheel==0.38.4", - "httpx==0.28.1", + "build==1.3.0", + "semantic-version==2.9.0", + "setuptools==78.1.1", + "typing-extensions==4.0.1", + "wheel==0.38.4", + "httpx==0.28.1", ] [tool.setuptools] @@ -24,12 +24,7 @@ package-dir = { "payjoin" = "src/payjoin" } include-package-data = true [tool.pytest.ini_options] -pythonpath = [ - "." -] +pythonpath = ["."] [dependency-groups] -dev = [ - "toml==0.10.2", - "yapf==0.43.0", -] +dev = ["toml==0.10.2", "yapf==0.43.0"] diff --git a/payjoin-ffi/python/setup.py b/payjoin-ffi/python/setup.py index e333628fd..3e33e278d 100644 --- a/payjoin-ffi/python/setup.py +++ b/payjoin-ffi/python/setup.py @@ -5,9 +5,9 @@ import toml # Read version from Cargo.toml -cargo_toml_path = os.path.join(os.path.dirname(__file__), '..', 'Cargo.toml') +cargo_toml_path = os.path.join(os.path.dirname(__file__), "..", "Cargo.toml") cargo_toml = toml.load(cargo_toml_path) -version = cargo_toml['package']['version'] +version = cargo_toml["package"]["version"] LONG_DESCRIPTION = """# payjoin This repository creates libraries for various programming languages, all using the Rust-based [Payjoin](https://github.com/payjoin/rust-payjoin) diff --git a/payjoin-ffi/python/test/test_payjoin_integration_test.py b/payjoin-ffi/python/test/test_payjoin_integration_test.py index 864af58bb..bd62d72fd 100644 --- a/payjoin-ffi/python/test/test_payjoin_integration_test.py +++ b/payjoin-ffi/python/test/test_payjoin_integration_test.py @@ -16,6 +16,7 @@ import unittest from pprint import * + class InMemoryReceiverSessionEventLog(JsonReceiverSessionPersister): def __init__(self, id): self.id = id @@ -31,6 +32,7 @@ def load(self): def close(self): self.closed = True + class InMemorySenderPersister(JsonSenderSessionPersister): def __init__(self, id): self.id = id @@ -55,9 +57,16 @@ def setUpClass(cls): cls.receiver = cls.env.get_receiver() cls.sender = cls.env.get_sender() - async def process_receiver_proposal(self, receiver: ReceiveSession, recv_persister: InMemoryReceiverSessionEventLog, ohttp_relay: str) -> Optional[ReceiveSession]: + async def process_receiver_proposal( + self, + receiver: ReceiveSession, + recv_persister: InMemoryReceiverSessionEventLog, + ohttp_relay: str, + ) -> Optional[ReceiveSession]: if receiver.is_INITIALIZED(): - res = await self.retrieve_receiver_proposal(receiver.inner, recv_persister, ohttp_relay) + res = await self.retrieve_receiver_proposal( + receiver.inner, recv_persister, ohttp_relay + ) if res is None: return None return res @@ -77,64 +86,126 @@ async def process_receiver_proposal(self, receiver: ReceiveSession, recv_persist if receiver.is_WANTS_FEE_RANGE(): return await self.process_wants_fee_range(receiver.inner, recv_persister) if receiver.is_PROVISIONAL_PROPOSAL(): - return await self.process_provisional_proposal(receiver.inner, recv_persister) + return await self.process_provisional_proposal( + receiver.inner, recv_persister + ) if receiver.is_PAYJOIN_PROPOSAL(): return receiver raise Exception(f"Unknown receiver state: {receiver}") - def create_receiver_context(self, receiver_address: bitcoinffi.Address, directory: str, ohttp_keys: OhttpKeys, recv_persister: InMemoryReceiverSessionEventLog) -> Initialized: - receiver = ReceiverBuilder(address=receiver_address, directory=directory, ohttp_keys=ohttp_keys).build().save(recv_persister) + def create_receiver_context( + self, + receiver_address: bitcoinffi.Address, + directory: str, + ohttp_keys: OhttpKeys, + recv_persister: InMemoryReceiverSessionEventLog, + ) -> Initialized: + receiver = ( + ReceiverBuilder( + address=receiver_address, directory=directory, ohttp_keys=ohttp_keys + ) + .build() + .save(recv_persister) + ) return receiver - async def retrieve_receiver_proposal(self, receiver: Initialized, recv_persister: InMemoryReceiverSessionEventLog, ohttp_relay: str): + async def retrieve_receiver_proposal( + self, + receiver: Initialized, + recv_persister: InMemoryReceiverSessionEventLog, + ohttp_relay: str, + ): agent = httpx.AsyncClient() request: RequestResponse = receiver.create_poll_request(ohttp_relay) response = await agent.post( url=request.request.url, headers={"Content-Type": request.request.content_type}, - content=request.request.body + content=request.request.body, + ) + res = receiver.process_response(response.content, request.client_response).save( + recv_persister ) - res = receiver.process_response(response.content, request.client_response).save(recv_persister) if res.is_STASIS(): return None return await self.process_unchecked_proposal(res.inner, recv_persister) - async def process_unchecked_proposal(self, proposal: UncheckedOriginalPayload, recv_persister: InMemoryReceiverSessionEventLog) : - receiver = proposal.check_broadcast_suitability(None, MempoolAcceptanceCallback(self.receiver)).save(recv_persister) + async def process_unchecked_proposal( + self, + proposal: UncheckedOriginalPayload, + recv_persister: InMemoryReceiverSessionEventLog, + ): + receiver = proposal.check_broadcast_suitability( + None, MempoolAcceptanceCallback(self.receiver) + ).save(recv_persister) return await self.process_maybe_inputs_owned(receiver, recv_persister) - async def process_maybe_inputs_owned(self, proposal: MaybeInputsOwned, recv_persister: InMemoryReceiverSessionEventLog): - maybe_inputs_owned = proposal.check_inputs_not_owned(IsScriptOwnedCallback(self.receiver)).save(recv_persister) + async def process_maybe_inputs_owned( + self, + proposal: MaybeInputsOwned, + recv_persister: InMemoryReceiverSessionEventLog, + ): + maybe_inputs_owned = proposal.check_inputs_not_owned( + IsScriptOwnedCallback(self.receiver) + ).save(recv_persister) return await self.process_maybe_inputs_seen(maybe_inputs_owned, recv_persister) - async def process_maybe_inputs_seen(self, proposal: MaybeInputsSeen, recv_persister: InMemoryReceiverSessionEventLog): - outputs_unknown = proposal.check_no_inputs_seen_before(CheckInputsNotSeenCallback(self.receiver)).save(recv_persister) + async def process_maybe_inputs_seen( + self, proposal: MaybeInputsSeen, recv_persister: InMemoryReceiverSessionEventLog + ): + outputs_unknown = proposal.check_no_inputs_seen_before( + CheckInputsNotSeenCallback(self.receiver) + ).save(recv_persister) return await self.process_outputs_unknown(outputs_unknown, recv_persister) - async def process_outputs_unknown(self, proposal: OutputsUnknown, recv_persister: InMemoryReceiverSessionEventLog): - wants_outputs = proposal.identify_receiver_outputs(IsScriptOwnedCallback(self.receiver)).save(recv_persister) + async def process_outputs_unknown( + self, proposal: OutputsUnknown, recv_persister: InMemoryReceiverSessionEventLog + ): + wants_outputs = proposal.identify_receiver_outputs( + IsScriptOwnedCallback(self.receiver) + ).save(recv_persister) return await self.process_wants_outputs(wants_outputs, recv_persister) - async def process_wants_outputs(self, proposal: WantsOutputs, recv_persister: InMemoryReceiverSessionEventLog): + async def process_wants_outputs( + self, proposal: WantsOutputs, recv_persister: InMemoryReceiverSessionEventLog + ): wants_inputs = proposal.commit_outputs().save(recv_persister) return await self.process_wants_inputs(wants_inputs, recv_persister) - async def process_wants_inputs(self, proposal: WantsInputs, recv_persister: InMemoryReceiverSessionEventLog): - provisional_proposal = proposal.contribute_inputs(get_inputs(self.receiver)).commit_inputs().save(recv_persister) + async def process_wants_inputs( + self, proposal: WantsInputs, recv_persister: InMemoryReceiverSessionEventLog + ): + provisional_proposal = ( + proposal.contribute_inputs(get_inputs(self.receiver)) + .commit_inputs() + .save(recv_persister) + ) return await self.process_wants_fee_range(provisional_proposal, recv_persister) - - async def process_wants_fee_range(self, proposal: WantsFeeRange, recv_persister: InMemoryReceiverSessionEventLog): + + async def process_wants_fee_range( + self, proposal: WantsFeeRange, recv_persister: InMemoryReceiverSessionEventLog + ): provisional_proposal = proposal.apply_fee_range(1, 10).save(recv_persister) - return await self.process_provisional_proposal(provisional_proposal, recv_persister) + return await self.process_provisional_proposal( + provisional_proposal, recv_persister + ) - async def process_provisional_proposal(self, proposal: ProvisionalProposal, recv_persister: InMemoryReceiverSessionEventLog): - payjoin_proposal = proposal.finalize_proposal(ProcessPsbtCallback(self.receiver)).save(recv_persister) + async def process_provisional_proposal( + self, + proposal: ProvisionalProposal, + recv_persister: InMemoryReceiverSessionEventLog, + ): + payjoin_proposal = proposal.finalize_proposal( + ProcessPsbtCallback(self.receiver) + ).save(recv_persister) return ReceiveSession.PAYJOIN_PROPOSAL(payjoin_proposal) async def test_integration_v2_to_v2(self): try: - receiver_address = bitcoinffi.Address(json.loads(self.receiver.call("getnewaddress", [])), bitcoinffi.Network.REGTEST) + receiver_address = bitcoinffi.Address( + json.loads(self.receiver.call("getnewaddress", [])), + bitcoinffi.Network.REGTEST, + ) init_tracing() services = TestServices.initialize() @@ -148,8 +219,12 @@ async def test_integration_v2_to_v2(self): # Inside the Receiver: recv_persister = InMemoryReceiverSessionEventLog(1) sender_persister = InMemorySenderPersister(1) - session = self.create_receiver_context(receiver_address, directory, ohttp_keys, recv_persister) - process_response = await self.process_receiver_proposal(ReceiveSession.INITIALIZED(session), recv_persister, ohttp_relay) + session = self.create_receiver_context( + receiver_address, directory, ohttp_keys, recv_persister + ) + process_response = await self.process_receiver_proposal( + ReceiveSession.INITIALIZED(session), recv_persister, ohttp_relay + ) self.assertIsNone(process_response) # ********************** @@ -157,21 +232,29 @@ async def test_integration_v2_to_v2(self): # Create a funded PSBT (not broadcasted) to address with amount given in the pj_uri pj_uri = session.pj_uri() psbt = build_sweep_psbt(self.sender, pj_uri) - req_ctx: WithReplyKey = SenderBuilder(psbt, pj_uri).build_recommended(1000).save(sender_persister) + req_ctx: WithReplyKey = ( + SenderBuilder(psbt, pj_uri) + .build_recommended(1000) + .save(sender_persister) + ) request: RequestOhttpContext = req_ctx.create_v2_post_request(ohttp_relay) response = await agent.post( url=request.request.url, headers={"Content-Type": request.request.content_type}, - content=request.request.body + content=request.request.body, ) - send_ctx: PollingForProposal = req_ctx.process_response(response.content, request.ohttp_ctx).save(sender_persister) + send_ctx: PollingForProposal = req_ctx.process_response( + response.content, request.ohttp_ctx + ).save(sender_persister) # POST Original PSBT # ********************** # Inside the Receiver: # GET fallback psbt - payjoin_proposal = await self.process_receiver_proposal(ReceiveSession.INITIALIZED(session), recv_persister, ohttp_relay) + payjoin_proposal = await self.process_receiver_proposal( + ReceiveSession.INITIALIZED(session), recv_persister, ohttp_relay + ) self.assertIsNotNone(payjoin_proposal) self.assertEqual(payjoin_proposal.is_PAYJOIN_PROPOSAL(), True) @@ -180,7 +263,7 @@ async def test_integration_v2_to_v2(self): response = await agent.post( url=request.request.url, headers={"Content-Type": request.request.content_type}, - content=request.request.body + content=request.request.body, ) payjoin_proposal.process_response(response.content, request.client_response) @@ -192,39 +275,76 @@ async def test_integration_v2_to_v2(self): response = await agent.post( url=request.request.url, headers={"Content-Type": request.request.content_type}, - content=request.request.body + content=request.request.body, + ) + checked_payjoin_proposal_psbt = ( + send_ctx.process_response(response.content, request.ohttp_ctx) + .save(sender_persister) + .inner ) - checked_payjoin_proposal_psbt = send_ctx.process_response(response.content, request.ohttp_ctx).save(sender_persister).inner print(f"checked_payjoin_proposal_psbt: {checked_payjoin_proposal_psbt}") self.assertIsNotNone(checked_payjoin_proposal_psbt) - payjoin_psbt = json.loads(self.sender.call("walletprocesspsbt", [checked_payjoin_proposal_psbt.serialize_base64()]))["psbt"] - final_psbt = json.loads(self.sender.call("finalizepsbt", [payjoin_psbt, json.dumps(False)]))["psbt"] + payjoin_psbt = json.loads( + self.sender.call( + "walletprocesspsbt", + [checked_payjoin_proposal_psbt.serialize_base64()], + ) + )["psbt"] + final_psbt = json.loads( + self.sender.call("finalizepsbt", [payjoin_psbt, json.dumps(False)]) + )["psbt"] payjoin_tx = bitcoinffi.Psbt.deserialize_base64(final_psbt).extract_tx() - self.sender.call("sendrawtransaction", [json.dumps(payjoin_tx.serialize().hex())]) + self.sender.call( + "sendrawtransaction", [json.dumps(payjoin_tx.serialize().hex())] + ) # Check resulting transaction and balances network_fees = bitcoinffi.Psbt.deserialize_base64(final_psbt).fee().to_btc() # Sender sent the entire value of their utxo to receiver (minus fees) - self.assertEqual(len(payjoin_tx.input()), 2); - self.assertEqual(len(payjoin_tx.output()), 1); - self.assertEqual(float(json.loads(self.receiver.call("getbalances", []))["mine"]["untrusted_pending"]), 100 - network_fees) + self.assertEqual(len(payjoin_tx.input()), 2) + self.assertEqual(len(payjoin_tx.output()), 1) + self.assertEqual( + float( + json.loads(self.receiver.call("getbalances", []))["mine"][ + "untrusted_pending" + ] + ), + 100 - network_fees, + ) self.assertEqual(float(self.sender.call("getbalance", [])), 0) return except Exception as e: print("Caught:", e) raise + def build_sweep_psbt(sender: RpcClient, pj_uri: PjUri) -> bitcoinffi.Psbt: outputs = {} outputs[pj_uri.address()] = 50 - psbt = json.loads(sender.call( - "walletcreatefundedpsbt", - [json.dumps([]), - json.dumps(outputs), - json.dumps(0), - json.dumps({"lockUnspents": True, "fee_rate": 10, "subtractFeeFromOutputs": [0]}) - ]))["psbt"] - return json.loads(sender.call("walletprocesspsbt", [psbt, json.dumps(True), json.dumps("ALL"), json.dumps(False)]))["psbt"] + psbt = json.loads( + sender.call( + "walletcreatefundedpsbt", + [ + json.dumps([]), + json.dumps(outputs), + json.dumps(0), + json.dumps( + { + "lockUnspents": True, + "fee_rate": 10, + "subtractFeeFromOutputs": [0], + } + ), + ], + ) + )["psbt"] + return json.loads( + sender.call( + "walletprocesspsbt", + [psbt, json.dumps(True), json.dumps("ALL"), json.dumps(False)], + ) + )["psbt"] + def get_inputs(rpc_connection: RpcClient) -> list[InputPair]: utxos = json.loads(rpc_connection.call("listunspent", [])) @@ -234,44 +354,60 @@ def get_inputs(rpc_connection: RpcClient) -> list[InputPair]: previous_output=bitcoinffi.OutPoint(txid=utxo["txid"], vout=utxo["vout"]), script_sig=bitcoinffi.Script(bytes()), sequence=0, - witness=[] + witness=[], + ) + raw_tx = json.loads( + rpc_connection.call( + "gettransaction", + [json.dumps(utxo["txid"]), json.dumps(True), json.dumps(True)], + ) ) - raw_tx = json.loads(rpc_connection.call("gettransaction", [json.dumps(utxo["txid"]), json.dumps(True), json.dumps(True)])) prev_out = raw_tx["decoded"]["vout"][utxo["vout"]] prev_spk = bitcoinffi.Script(bytes.fromhex(prev_out["scriptPubKey"]["hex"])) prev_amount = bitcoinffi.Amount.from_btc(prev_out["value"]) tx_out = bitcoinffi.TxOut(value=prev_amount, script_pubkey=prev_spk) - psbt_in = PsbtInput(witness_utxo=tx_out, redeem_script=None, witness_script=None) + psbt_in = PsbtInput( + witness_utxo=tx_out, redeem_script=None, witness_script=None + ) inputs.append(InputPair(txin=txin, psbtin=psbt_in, expected_weight=None)) return inputs + class MempoolAcceptanceCallback(CanBroadcast): def __init__(self, connection: RpcClient): self.connection = connection def callback(self, tx): - try: - res = json.loads(self.connection.call("testmempoolaccept", [json.dumps([bytes(tx).hex()])]))[0][ - "allowed" - ] - return res - except Exception as e: + try: + res = json.loads( + self.connection.call( + "testmempoolaccept", [json.dumps([bytes(tx).hex()])] + ) + )[0]["allowed"] + return res + except Exception as e: print(f"An error occurred: {e}") return None + class IsScriptOwnedCallback(IsScriptOwned): def __init__(self, connection: RpcClient): self.connection = connection def callback(self, script): try: - address = bitcoinffi.Address.from_script(bitcoinffi.Script(script), bitcoinffi.Network.REGTEST) - return json.loads(self.connection.call("getaddressinfo", [str(address)]))["ismine"] + address = bitcoinffi.Address.from_script( + bitcoinffi.Script(script), bitcoinffi.Network.REGTEST + ) + return json.loads(self.connection.call("getaddressinfo", [str(address)]))[ + "ismine" + ] except Exception as e: print(f"An error occurred: {e}") return None + class CheckInputsNotSeenCallback(IsOutputKnown): def __init__(self, connection: RpcClient): self.connection = connection @@ -279,13 +415,15 @@ def __init__(self, connection: RpcClient): def callback(self, _outpoint): return False + class ProcessPsbtCallback(ProcessPsbt): def __init__(self, connection: RpcClient): self.connection = connection def callback(self, psbt: str): res = json.loads(self.connection.call("walletprocesspsbt", [psbt])) - return res['psbt'] + return res["psbt"] + if __name__ == "__main__": unittest.main() diff --git a/payjoin-ffi/python/test/test_payjoin_unit_test.py b/payjoin-ffi/python/test/test_payjoin_unit_test.py index 1867a4242..e18ac25e3 100644 --- a/payjoin-ffi/python/test/test_payjoin_unit_test.py +++ b/payjoin-ffi/python/test/test_payjoin_unit_test.py @@ -1,6 +1,7 @@ import unittest import payjoin -import payjoin.bitcoin +import payjoin.bitcoin + class TestURIs(unittest.TestCase): def test_todo_url_encoded(self): @@ -31,6 +32,7 @@ def test_valid_uris(self): except Exception as e: self.fail(f"Failed to create a valid Uri for {uri}. Error: {e}") + class InMemoryReceiverPersister(payjoin.JsonReceiverSessionPersister): def __init__(self, id): self.id = id @@ -46,18 +48,26 @@ def load(self): def close(self): self.closed = True + class TestReceiverPersistence(unittest.TestCase): def test_receiver_persistence(self): persister = InMemoryReceiverPersister(1) - address = payjoin.bitcoin.Address("tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", payjoin.bitcoin.Network.SIGNET) + address = payjoin.bitcoin.Address( + "tb1q6d3a2w975yny0asuvd9a67ner4nks58ff0q8g4", payjoin.bitcoin.Network.SIGNET + ) payjoin.ReceiverBuilder( - address, - "https://example.com", - payjoin.OhttpKeys.decode(bytes.fromhex("01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003")), - ).build().save(persister) + address, + "https://example.com", + payjoin.OhttpKeys.decode( + bytes.fromhex( + "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" + ) + ), + ).build().save(persister) result = payjoin.replay_receiver_event_log(persister) self.assertTrue(result.state().is_INITIALIZED()) + class InMemorySenderPersister(payjoin.JsonSenderSessionPersister): def __init__(self, id): self.id = id @@ -73,21 +83,35 @@ def load(self): def close(self): self.closed = True + class TestSenderPersistence(unittest.TestCase): def test_sender_persistence(self): # Create a receiver to just get the pj uri persister = InMemoryReceiverPersister(1) - address = payjoin.bitcoin.Address("2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", payjoin.bitcoin.Network.TESTNET) - receiver = payjoin.ReceiverBuilder( - address, - "https://example.com", - payjoin.OhttpKeys.decode(bytes.fromhex("01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003")), - ).build().save(persister) + address = payjoin.bitcoin.Address( + "2MuyMrZHkbHbfjudmKUy45dU4P17pjG2szK", payjoin.bitcoin.Network.TESTNET + ) + receiver = ( + payjoin.ReceiverBuilder( + address, + "https://example.com", + payjoin.OhttpKeys.decode( + bytes.fromhex( + "01001604ba48c49c3d4a92a3ad00ecc63a024da10ced02180c73ec12d8a7ad2cc91bb483824fe2bee8d28bfe2eb2fc6453bc4d31cd851e8a6540e86c5382af588d370957000400010003" + ) + ), + ) + .build() + .save(persister) + ) uri = receiver.pj_uri() persister = InMemorySenderPersister(1) psbt = "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA=" - with_reply_key = payjoin.SenderBuilder(psbt, uri).build_recommended(1000).save(persister) - + with_reply_key = ( + payjoin.SenderBuilder(psbt, uri).build_recommended(1000).save(persister) + ) + + if __name__ == "__main__": unittest.main() diff --git a/payjoin-test-utils/CHANGELOG.md b/payjoin-test-utils/CHANGELOG.md index 998a6025a..378dab084 100644 --- a/payjoin-test-utils/CHANGELOG.md +++ b/payjoin-test-utils/CHANGELOG.md @@ -2,7 +2,7 @@ ## 0.0.1 -- Export InMemoryTestPersister under _test-utils [#761](https://github.com/payjoin/rust-payjoin/pull/761) +- Export InMemoryTestPersister under \_test-utils [#761](https://github.com/payjoin/rust-payjoin/pull/761) - Introduce constructors for SegWit input pairs [#712](https://github.com/payjoin/rust-payjoin/pull/712) - Move testing constants to payjoin-test-utils [#613](https://github.com/payjoin/rust-payjoin/pull/613) - Specify the versions for deps more precisely in the cargo toml [#696](https://github.com/payjoin/rust-payjoin/pull/696) diff --git a/payjoin-test-utils/Cargo.toml b/payjoin-test-utils/Cargo.toml index 8cf354c88..17f2f947b 100644 --- a/payjoin-test-utils/Cargo.toml +++ b/payjoin-test-utils/Cargo.toml @@ -15,12 +15,18 @@ http = "1.3.1" ohttp = { package = "bitcoin-ohttp", version = "0.6.0" } ohttp-relay = { version = "0.0.11", features = ["_test-util"] } once_cell = "1.21.3" -payjoin = { version = "1.0.0-rc.1", features = ["io", "_manual-tls", "_test-utils"] } +payjoin = { version = "1.0.0-rc.1", features = [ + "io", + "_manual-tls", + "_test-utils", +] } payjoin-directory = { version = "0.0.3", features = ["_manual-tls"] } rcgen = "0.14.3" -rustls = { version = "0.23.31", default-features=false, features = ["ring"] } -reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls"] } +reqwest = { version = "0.12.23", default-features = false, features = [ + "rustls-tls", +] } +rustls = { version = "0.23.31", default-features = false, features = ["ring"] } +tempfile = "3.20.0" tokio = { version = "1.47.1", features = ["full"] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -tempfile = "3.20.0" diff --git a/payjoin/CHANGELOG.md b/payjoin/CHANGELOG.md index 0378b22f0..ba797c1ea 100644 --- a/payjoin/CHANGELOG.md +++ b/payjoin/CHANGELOG.md @@ -92,13 +92,11 @@ Selected Improvements: - Sender session history fallback (#805) - 0.24 name audit (#803, #810) - ### Better ergonomics - Introduce constructors for SegWit input pairs (#712) - Introduce constructors for legacy input pairs (#753) - ### Organize for readability - Update README title and add logo & badges (#665) @@ -109,29 +107,27 @@ Selected Improvements: - Dedupe ImplementationError (#669) - Clean up re-exports (#746) - ### Various Operational improvements - Randomly pad OHTTP requests (#715) - Limit response sizes for v1 (#586) - ## 0.23.0 -- Make features additive [#430](https://github.com/payjoin/rust-payjoin/pull/430) [#466](https://github.com/payjoin/rust-payjoin/pull/466) [#501](https://github.com/payjoin/rust-payjoin/pull/501) [#518](https://github.com/payjoin/rust-payjoin/pull/518) -- Make receiver errors replyable to the sender [#474](https://github.com/payjoin/rust-payjoin/pull/474) [#506](https://github.com/payjoin/rust-payjoin/pull/506) [#526](https://github.com/payjoin/rust-payjoin/pull/526) [#606](https://github.com/payjoin/rust-payjoin/pull/606) -- Separate error modules [#482](https://github.com/payjoin/rust-payjoin/pull/482) -- Introduce "directory" feature module [#502](https://github.com/payjoin/rust-payjoin/pull/502) -- Expose test helpers via payjoin-test-utils crate [#484](https://github.com/payjoin/rust-payjoin/pull/484) -- Accommodate updated BIP78 spec [#505](https://github.com/payjoin/rust-payjoin/pull/505) -- Fallback to first candidate if avoid_uih fails [#533](https://github.com/payjoin/rust-payjoin/pull/533) -- Use IntoUrl trait instead of Url in function signatures [#520](https://github.com/payjoin/rust-payjoin/pull/520) -- Don't accept invalid certs even in tests [#550](https://github.com/payjoin/rust-payjoin/pull/550) -- Introduce experimental multiparty sender behind the "_multiparty" feature flag [#434](https://github.com/payjoin/rust-payjoin/pull/434) -- Add support for RFC 9540 ohttp-keys fetching and decentralized BIP 77 directory opt-in [#549](https://github.com/payjoin/rust-payjoin/pull/549) [#570](https://github.com/payjoin/rust-payjoin/pull/570) [#587](https://github.com/payjoin/rust-payjoin/pull/587) -- Fix the `pjos` BIP21 parameter to match the BIP78 spec [#546](https://github.com/payjoin/rust-payjoin/pull/546) -- Introduce mutation testing [#573](https://github.com/payjoin/rust-payjoin/pull/573) -- Add first-class persistence abstraction [#552](https://github.com/payjoin/rust-payjoin/pull/552) +- Make features additive [#430](https://github.com/payjoin/rust-payjoin/pull/430) [#466](https://github.com/payjoin/rust-payjoin/pull/466) [#501](https://github.com/payjoin/rust-payjoin/pull/501) [#518](https://github.com/payjoin/rust-payjoin/pull/518) +- Make receiver errors replyable to the sender [#474](https://github.com/payjoin/rust-payjoin/pull/474) [#506](https://github.com/payjoin/rust-payjoin/pull/506) [#526](https://github.com/payjoin/rust-payjoin/pull/526) [#606](https://github.com/payjoin/rust-payjoin/pull/606) +- Separate error modules [#482](https://github.com/payjoin/rust-payjoin/pull/482) +- Introduce "directory" feature module [#502](https://github.com/payjoin/rust-payjoin/pull/502) +- Expose test helpers via payjoin-test-utils crate [#484](https://github.com/payjoin/rust-payjoin/pull/484) +- Accommodate updated BIP78 spec [#505](https://github.com/payjoin/rust-payjoin/pull/505) +- Fallback to first candidate if avoid_uih fails [#533](https://github.com/payjoin/rust-payjoin/pull/533) +- Use IntoUrl trait instead of Url in function signatures [#520](https://github.com/payjoin/rust-payjoin/pull/520) +- Don't accept invalid certs even in tests [#550](https://github.com/payjoin/rust-payjoin/pull/550) +- Introduce experimental multiparty sender behind the "\_multiparty" feature flag [#434](https://github.com/payjoin/rust-payjoin/pull/434) +- Add support for RFC 9540 ohttp-keys fetching and decentralized BIP 77 directory opt-in [#549](https://github.com/payjoin/rust-payjoin/pull/549) [#570](https://github.com/payjoin/rust-payjoin/pull/570) [#587](https://github.com/payjoin/rust-payjoin/pull/587) +- Fix the `pjos` BIP21 parameter to match the BIP78 spec [#546](https://github.com/payjoin/rust-payjoin/pull/546) +- Introduce mutation testing [#573](https://github.com/payjoin/rust-payjoin/pull/573) +- Add first-class persistence abstraction [#552](https://github.com/payjoin/rust-payjoin/pull/552) - Add many more tests, reaching [82%](https://coveralls.io/builds/73029930) coverage - up from 60% when coverage reports were introduced. ## 0.22.0 @@ -148,7 +144,7 @@ Selected Improvements: - Make `InputPair` public to facilitate working with inputs in coin selection and input contributions - Enable receiver fee contributions in `apply_fee`, which now requires a max_feerate parameter - Fix weight estimations for nested segwit inputs -- Fix mixed input scripts receiver check in Payjoin V1 to only error if the receiver would *introduce* mixed types +- Fix mixed input scripts receiver check in Payjoin V1 to only error if the receiver would _introduce_ mixed types - Allow mixed input scripts in Payjoin V2 - Implement client end-to-end encryption using HPKE using [bitcoin-hpke](https://docs.rs/bitcoin-hpke/latest/bitcoin_hpke/) - Make session initialization implicit @@ -161,7 +157,6 @@ Selected Improvements: - Work around '#' escaping bug in bip21 crate [#373](https://github.com/payjoin/rust-payjoin/pull/373) - Hide `_danger-local-https` feature behind `_` prefix so it doesn't show up in docs [#423](https://github.com/payjoin/rust-payjoin/pull/423) - ## 0.20.0 - remove `contribute_non_witness_input` because it was unused @@ -186,13 +181,13 @@ This release attempts to stabilize the Payjoin V2 Bitcoin URI format. That inclu - Handle OHTTP encapsulated response status ([#284](https://github.com/payjoin/rust-payjoin/pull/284)) - Upgrade `receive::v2` Typestate machine to resume multiple payjoins simultaneously ([#283](https://github.com/payjoin/rust-payjoin/pull/283)) - - `Enroller` became `SessionInitializer` - - `Enrolled` became `ActiveSession` - - `fallback_target()` became `pj_url()` - - `pj_url_builder()` was introduced - - `ContextV2` became `SessionContext` - - Include a bitcoin address in `SessionContext` - - Document it all ([#308](https://github.com/payjoin/rust-payjoin/pull/308)) + - `Enroller` became `SessionInitializer` + - `Enrolled` became `ActiveSession` + - `fallback_target()` became `pj_url()` + - `pj_url_builder()` was introduced + - `ContextV2` became `SessionContext` + - Include a bitcoin address in `SessionContext` + - Document it all ([#308](https://github.com/payjoin/rust-payjoin/pull/308)) - `send::ResponseError` variants fields got explicit names ([#304](https://github.com/payjoin/rust-payjoin/pull/304)) - Refactor output substitution with new fallable `try_substitute_outputs` ([#277](https://github.com/payjoin/rust-payjoin/pull/277)) @@ -239,7 +234,7 @@ This release attempts to stabilize the Payjoin V2 Bitcoin URI format. That inclu - Handle `supported` versions in `ResponseError` - Make `RequestContext`, `RequestBuilder` `Clone` - Expose v2 mod internally to `pub(crate)` only -- Use typesafe KeyConfig for ohttp d/encapsulation +- Use typesafe KeyConfig for ohttp d/encapsulation - Use spec OHTTP media types - Build PjUri with PjUriBuilder (#185) - Parse and pass urls as `Url` instead of `String` diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 79b326b87..61dc7bc58 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -6,7 +6,11 @@ description = "Payjoin Library implementing BIP 78 and BIP 77 batching protocols repository = "https://github.com/payjoin/rust-payjoin" readme = "../README.md" keywords = ["bip78", "payjoin", "bitcoin"] -categories = ["api-bindings", "cryptography::cryptocurrencies", "network-programming"] +categories = [ + "api-bindings", + "cryptography::cryptocurrencies", + "network-programming", +] license = "MITNFA" resolver = "2" edition = "2021" @@ -18,7 +22,15 @@ exclude = ["tests"] [features] default = ["v2"] #[doc = "Core features for payjoin state machines"] -_core = ["bitcoin/rand-std", "dep:http", "serde_json", "url/serde", "bitcoin_uri", "serde", "bitcoin/serde"] +_core = [ + "bitcoin/rand-std", + "dep:http", + "serde_json", + "url/serde", + "bitcoin_uri", + "serde", + "bitcoin/serde", +] directory = [] v1 = ["_core"] v2 = ["_core", "hpke", "bhttp", "ohttp", "directory"] @@ -28,25 +40,29 @@ _manual-tls = ["reqwest/rustls-tls", "rustls"] _test-utils = [] [dependencies] +bhttp = { version = "0.6.1", optional = true } bitcoin = { version = "0.32.7", features = ["base64"] } bitcoin_uri = { version = "0.1.0", optional = true } hpke = { package = "bitcoin-hpke", version = "0.13.0", optional = true } http = { version = "1.3.1", optional = true } -bhttp = { version = "0.6.1", optional = true } ohttp = { package = "bitcoin-ohttp", version = "0.6.0", optional = true } -serde = { version = "1.0.219", default-features = false, optional = true } reqwest = { version = "0.12.23", default-features = false, optional = true } -rustls = { version = "0.23.31", optional = true, default-features=false, features = ["ring"] } -url = { version = "2.5.4", optional = true, default-features=false, features = ["serde"] } +rustls = { version = "0.23.31", optional = true, default-features = false, features = [ + "ring", +] } +serde = { version = "1.0.219", default-features = false, optional = true } serde_json = { version = "1.0.142", optional = true } tracing = "0.1.41" +url = { version = "2.5.4", optional = true, default-features = false, features = [ + "serde", +] } [target.'cfg(target_arch = "wasm32")'.dependencies] web-time = "1.1.0" [dev-dependencies] -payjoin-test-utils = { version = "0.0.1" } once_cell = "1.21.3" +payjoin-test-utils = { version = "0.0.1" } tokio = { version = "1.47.1", features = ["full"] } tracing = "0.1.41" diff --git a/taplo.toml b/taplo.toml new file mode 100644 index 000000000..324fc69b1 --- /dev/null +++ b/taplo.toml @@ -0,0 +1,13 @@ +# Exclude paths from formatting +exclude = ["target/**", ".direnv/**", ".git/**"] + +[formatting] +indent_style = "space" +indent_size = 2 + +[[rule]] +include = ["**/Cargo.toml"] +keys = ["dependencies", "*-dependencies", "workspace"] +[rule.formatting] +reorder_keys = true +align_comments = true From 45a17f8a2b16f359c180da6eba7c6bed258117aa Mon Sep 17 00:00:00 2001 From: user Date: Fri, 12 Dec 2025 11:55:08 -0500 Subject: [PATCH 2/4] Ignore large formatting commits for git blame sanity when using git blame you will need to ensure that this ignore file is respected with `git blame --ignore-revs-file ` to ensure all commits listed there are properly ignored. --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..c4cd789d9 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +d9aa8187499a28f1244d8a57d2b474efabd3c1a1 +4157a3babacca1a63fac12364446d79aa464b378 From f23e74b9861b49eb337ccc1807513ae1ccfeffcd Mon Sep 17 00:00:00 2001 From: user Date: Wed, 10 Dec 2025 15:15:06 -0500 Subject: [PATCH 3/4] Use treefmt-nix in nix flake as universal formatter This commit adds treefmt-nix as a universal formatter to enforce formatting across all languages in the mono repo. This adds support for formatting dart, javascript, toml, and python beyond out current existing formatters. --- .github/workflows/rust.yml | 4 +-- flake.lock | 23 +++++++++++++++- flake.nix | 54 ++++++++++++++------------------------ treefmt.nix | 42 +++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 treefmt.nix diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1c0f6429a..8605050ad 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -53,7 +53,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: "Run formatting check" run: cargo fmt --all -- --check - + Format-FFI: runs-on: ubuntu-latest steps: @@ -67,7 +67,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: "Run formatting check" run: cd payjoin-ffi && cargo fmt --all -- --check - + Lint-FFI: runs-on: ubuntu-latest steps: diff --git a/flake.lock b/flake.lock index 249a29b5b..459722f1d 100644 --- a/flake.lock +++ b/flake.lock @@ -54,7 +54,8 @@ "crane": "crane", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay", + "treefmt-nix": "treefmt-nix" } }, "rust-overlay": { @@ -91,6 +92,26 @@ "repo": "default", "type": "github" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762938485, + "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index e8ba0bc0d..6c7cab5eb 100644 --- a/flake.nix +++ b/flake.nix @@ -9,6 +9,10 @@ inputs.nixpkgs.follows = "nixpkgs"; }; crane.url = "github:ipetkov/crane"; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -18,13 +22,23 @@ flake-utils, rust-overlay, crane, + treefmt-nix, }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; - overlays = [ rust-overlay.overlays.default ]; + overlays = [ + rust-overlay.overlays.default + (final: prev: { + rustToolchains = { + msrv = prev.rust-bin.stable.${msrv-version}.default; + stable = prev.rust-bin.stable.latest.default; + nightly = prev.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + }; + }) + ]; }; msrv-version = "1.85.0"; @@ -103,6 +117,8 @@ ) ) craneLibVersions; + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + fileSetForCrate = subdir: pkgs.lib.fileset.toSource { @@ -186,7 +202,7 @@ devShells = devShells // { default = devShells.nightly; }; - formatter = pkgs.nixfmt-tree; + formatter = treefmtEval.config.build.wrapper; checks = packages // (pkgs.lib.mapAttrs' ( @@ -249,42 +265,12 @@ } ); - nix-fmt-check = simpleCheck { - name = "nix-fmt-check"; - src = pkgs.lib.sources.sourceFilesBySuffices ./. [ ".nix" ]; - nativeBuildInputs = [ pkgs.nixfmt-tree ]; - checkPhase = '' - treefmt --ci --tree-root . - ''; - }; - - shfmt = simpleCheck rec { - name = "shfmt"; - src = pkgs.lib.sources.sourceFilesBySuffices ./. [ ".sh" ]; - nativeBuildInputs = [ pkgs.shfmt ]; - checkPhase = '' - shfmt -d -s -i 4 -ci ${src} - ''; - }; - - shellcheck = simpleCheck rec { - name = "shellcheck"; - src = pkgs.lib.sources.sourceFilesBySuffices ./. [ ".sh" ]; - nativeBuildInputs = [ - pkgs.shellcheck - pkgs.findutils - ]; - checkPhase = '' - find "${src}" -name '*.sh' -print0 | xargs -0 shellcheck -x - ''; - }; + formatting = treefmtEval.config.build.check self; quick = checkSuite "quick" ( with self.outputs.checks.${system}; [ - shfmt - shellcheck - nix-fmt-check + formatting ] ); diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 000000000..979643b86 --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,42 @@ +{ pkgs, ... }: +{ + projectRootFile = "flake.nix"; + programs = { + dart-format.enable = true; + nixfmt.enable = true; + prettier.enable = true; + ruff-format.enable = true; + rustfmt = { + enable = true; + package = pkgs.rustToolchains.nightly; + edition = "2018"; + }; + shellcheck.enable = true; + shfmt = { + enable = true; + indent_size = 4; + }; + taplo.enable = true; + }; + settings = { + formatter = { + dart-format.options = [ + "--language-version" + "latest" + ]; # https://github.com/dart-lang/sdk/issues/60163#issuecomment-2668274823 + rustfmt.options = [ + "--config-path" + "./rustfmt.toml" + ]; + shellcheck.includes = [ "*.sh" ]; + shellcheck.excludes = [ "*.envrc" ]; + shfmt = { + includes = [ "*.sh" ]; + excludes = [ "*.envrc" ]; + options = [ + "--case-indent" + ]; + }; + }; + }; +} From 3fff1aa767eea3ac79015171fde403c1a60b3b26 Mon Sep 17 00:00:00 2001 From: user Date: Wed, 10 Dec 2025 17:09:23 -0500 Subject: [PATCH 4/4] Enforce formatting in the CI utilizing nix fmt to format all languages --- .github/workflows/format.yml | 22 ++++++++++++++++++++++ .github/workflows/rust.yml | 28 ---------------------------- 2 files changed, 22 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/format.yml diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 000000000..ed962af4e --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,22 @@ +name: Continuous integration + +on: [push, pull_request] + +jobs: + Format: + runs-on: ubuntu-latest + steps: + - name: "Checkout repo" + uses: actions/checkout@v4 + - name: "Install nix" + uses: DeterminateSystems/determinate-nix-action@main + - name: "Use nix cache" + uses: DeterminateSystems/magic-nix-cache-action@main + - name: Run treefmt + run: | + set -eo pipefail + nix fmt -- --ci || { + echo "❌ Code is not formatted. Showing diff:" + git --no-pager diff + exit 1 + } diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8605050ad..ba38cee11 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -40,34 +40,6 @@ jobs: - name: Run tests run: RUST_LOG=debug bash contrib/test.sh - Format: - runs-on: ubuntu-latest - steps: - - name: "Checkout repo" - uses: actions/checkout@v4 - - name: "Install nightly toolchain" - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt - - name: "Use cache" - uses: Swatinem/rust-cache@v2 - - name: "Run formatting check" - run: cargo fmt --all -- --check - - Format-FFI: - runs-on: ubuntu-latest - steps: - - name: "Checkout repo" - uses: actions/checkout@v4 - - name: "Install nightly toolchain" - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt - - name: "Use cache" - uses: Swatinem/rust-cache@v2 - - name: "Run formatting check" - run: cd payjoin-ffi && cargo fmt --all -- --check - Lint-FFI: runs-on: ubuntu-latest steps: