From 527f4030f1b9d49972519905f3485ce824bded14 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 29 Sep 2026 20:23:57 +0200 Subject: [PATCH 1/3] feat: add payment request fixture services for wallet journeys --- CHANGELOG.md | 1 + README.md | 65 + docker-compose.yml | 31 + payment-requests/.dockerignore | 1 + payment-requests/Cargo.lock | 3514 ++++++++++++++++++++++++++++++++ payment-requests/Cargo.toml | 17 + payment-requests/Dockerfile | 14 + payment-requests/prepare | 130 ++ payment-requests/src/main.rs | 530 +++++ 9 files changed, 4303 insertions(+) create mode 100644 payment-requests/.dockerignore create mode 100644 payment-requests/Cargo.lock create mode 100644 payment-requests/Cargo.toml create mode 100644 payment-requests/Dockerfile create mode 100755 payment-requests/prepare create mode 100644 payment-requests/src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 88eb03a..b930b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Pubky marketplace test fixture behind the `marketplace` compose profile: Pubky testnet, Paykit Server `722ef268` built from pinned source, and the `pubky-marketplace` driver CLI (`up`, `seed`, `purchase`, `mine`, `verify`, `seller-auth`) for the Bitkit marketplace wallet journey with a headless or Bitkit-approved seller; see `docs/pubky-marketplace.md` +- Add opt-in rc56 Payment Request fixture services for linked-peer and deadline-history wallet journeys - Homegate Docker Compose service with dedicated PostgreSQL storage, local homeserver admin mock, and README setup flow - Repo-managed Trezor User Env Docker service and `scripts/trezor-emulator` helper for quickly smoke-testing Bitkit app Trezor PRs - Support `amount_msat` query param in `/generate/bolt11` endpoint for sub-sat precision invoices diff --git a/README.md b/README.md index 55d27f8..587535c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A complete Docker-based development environment for Bitcoin and Lightning Networ - **VSS Server**: Versioned Storage Server for app and ldk-node state backups - **Homegate**: Pubky Homeserver signup gatekeeper with local admin API mock - **Pubky marketplace fixture** (opt-in `marketplace` profile): Pubky testnet, Paykit Server and a purchase driver for the marketplace wallet journey +- **Payment Request fixture** (opt-in `payment-requests` profile): rc56 issuer and controlled peer on the marketplace Pubky testnet ## Quick Start @@ -210,6 +211,70 @@ docker compose logs -f bitcoind ### Bitkit Testing +#### Payment Requests and rc56 Deadline History + +The `payment-requests` profile starts two disposable Paykit rc56 SDK peers on +the marketplace fixture's Pubky testnet. `fixture-issuer` publishes a regtest +Paykit endpoint and sends one-time requests. `rc56-peer` can accept, reject, +cancel and pay requests through the shared regtest Bitcoin node. Plain +`docker compose up -d` does not start either peer. The commands below need +`curl`, `jq` and `python3` on the host. + +```bash +./pubky-marketplace up +./pubky-marketplace seed +docker compose --profile marketplace --profile payment-requests build fixture-issuer +docker compose --profile marketplace --profile payment-requests up -d --no-build fixture-issuer rc56-peer +curl -fsS http://127.0.0.1:3012/health | jq +curl -fsS http://127.0.0.1:3013/health | jq +``` + +Each `/health` response gives the identity, receiver path and published +`btc-regtest-p2wpkh` address. Prepare and verify all one-time J1 states plus +an accepted monthly subscription with one paid period and a new monthly +proposal: + +```bash +./payment-requests/prepare | jq +``` + +The command returns every request id and the regtest txids after checking the +peer's SDK states. `/pay` sends a transaction and a txid proof. If proof +delivery fails after the transaction was sent, retry queued delivery with +`POST /sync`; `/proof` accepts an existing wallet txid, address and amount. + +To test Bitkit, use a disposable app identity on this local Pubky testnet. +Link it to the issuer's `pubky` and `receiver_path`, then `POST /link` with +`mode: "accept"` on the issuer using the wallet's `peer_pubky` and +`peer_path`. Call `POST /sync` while the app advances its handshake. Send +`POST /request` to the app identity; the default actual-payment deadline is +seven days ahead, or set `deadline_at` to a UTC RFC3339 timestamp. The app +must synchronize the request and verify its own history row. To prepare +rejected or canceled records, issue another request and call `/reject` or +`/cancel` with its id from the payer side. For monthly requests, +`POST /request` accepts `monthly_starts_at` (UTC RFC3339) and +`period_start_deadline_seconds`; `/pay` then needs +`billing_period_start` and `billing_period_end`. + +Example one-time issuance to a linked app after both sides report `Linked`: + +```bash +APP_PUBKY=pubky... # replace with the disposable app identity +curl -fsS -X POST http://127.0.0.1:3012/request -H 'content-type: application/json' \ + -d "$(jq -nc --arg pubky "$APP_PUBKY" '{peer_pubky:$pubky,peer_path:"bitkit/wallet",amount_sats:15000,reference:"rc56-app-history"}')" | jq +``` + +These peers keep their identities and SDK records in memory. After a restart, +remove `fixture-issuer` and `rc56-peer` with +`docker compose --profile marketplace --profile payment-requests rm -sf fixture-issuer rc56-peer`, +then run `./pubky-marketplace reset` and relink the app. `./pubky-marketplace seed` +needs outbound internet for Paykit Server setup; the rc56 peer calls use the +local testnet. The lane still needs a Bitkit build pointed at the local Pubky +testnet and to verify the requested rows on device. The headless preparation +command does not populate a separate Bitkit identity's history; accepted and +paid app rows require the lane's controlled client to prepare those records +with the app's identity or an app build that supports importing fixture state. + #### Trezor Hardware PRs Use this section as the entry point when checking Bitkit app PRs or merged features that need the official Trezor emulator. Start by preparing the deterministic Trezor User Env: diff --git a/docker-compose.yml b/docker-compose.yml index 06685c8..ebe2c80 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -380,6 +380,8 @@ services: - "127.0.0.1:6287:6287" # homeserver Pubky TLS - "127.0.0.1:${MARKETPLACE_HOMESERVER_ADMIN_PORT:-16288}:6288" # homeserver admin (6288 is homegate's) - "127.0.0.1:${MARKETPLACE_PAYKIT_PORT:-3001}:3001" # paykit-server, shares this namespace + - "127.0.0.1:3012:3012" # opt-in fixture-issuer, shares this namespace + - "127.0.0.1:3013:3013" # opt-in rc56-peer, shares this namespace # Paykit Server 722ef268 (v0.1.0-rc4), built from source with the upstream # Dockerfile.local. Its setup flow emits the Pubky grant auth URL (cid and cpk) @@ -451,6 +453,35 @@ services: - ./.marketplace/evidence:/evidence entrypoint: ["node", "/app/driver.mjs"] + # The rc56 SDK peers share the marketplace testnet namespace and regtest chain. + fixture-issuer: + profiles: [payment-requests] + image: bitkit-docker/payment-request-fixture:rc56-24162ebb + build: ./payment-requests + restart: "no" + network_mode: service:pubky-testnet + depends_on: + - pubky-testnet + - bitcoind + environment: + FIXTURE_ROLE: fixture-issuer + FIXTURE_PORT: 3012 + BITCOIN_RPC_URL: http://bitcoind:43782 + + rc56-peer: + profiles: [payment-requests] + image: bitkit-docker/payment-request-fixture:rc56-24162ebb + build: ./payment-requests + restart: "no" + network_mode: service:pubky-testnet + depends_on: + - pubky-testnet + - bitcoind + environment: + FIXTURE_ROLE: rc56-peer + FIXTURE_PORT: 3013 + BITCOIN_RPC_URL: http://bitcoind:43782 + volumes: bitcoin_home: postgres_data: diff --git a/payment-requests/.dockerignore b/payment-requests/.dockerignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/payment-requests/.dockerignore @@ -0,0 +1 @@ +target diff --git a/payment-requests/Cargo.lock b/payment-requests/Cargo.lock new file mode 100644 index 0000000..1e35778 --- /dev/null +++ b/payment-requests/Cargo.lock @@ -0,0 +1,3514 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures 0.2.17", + "password-hash", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "async-compat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c97d7ff3c25d6c10d64170c12acaf5d4245e76dece3779c1d92b153a64f11df" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + +[[package]] +name = "base32" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "serde", + "unicode-normalization", + "zeroize", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitkit-payment-request-fixture" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "axum", + "chrono", + "paykit-lib", + "paykit-sdk", + "pubky", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake3" +version = "1.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" +dependencies = [ + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.1", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20 0.9.1", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", + "zeroize", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "cordyceps" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9ab7e0ca1d179628fa0172b2b97203c7fa0cd81be2448bd446fb9559ca9261" +dependencies = [ + "loom", + "tracing", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-queue" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "crypto_secretbox" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1" +dependencies = [ + "aead", + "cipher", + "generic-array", + "poly1305", + "salsa20", + "subtle", + "zeroize", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "der" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "diatomic-waker" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "doxygen-rs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9" +dependencies = [ + "phf", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "pkcs8", + "serdect", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" +dependencies = [ + "curve25519-dalek 5.0.0", + "ed25519", + "serde", + "sha2 0.11.0", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.9", +] + +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.9", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-buffered" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4421cb78ee172b6b06080093479d3c50f058e7c81b7d577bbb8d118d551d4cd5" +dependencies = [ + "cordyceps", + "diatomic-waker", + "futures-core", + "pin-project-lite", + "spin 0.10.1", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "futures-core", + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "generator" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ade96dc9003043bce7c035c85a9df5a858bfb2039c5a2e6fdf00f324f6c551" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.9", + "stable_deref_trait", +] + +[[package]] +name = "heed" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a56c94661ddfb51aa9cdfbf102cfcc340aa69267f95ebccc4af08d7c530d393" +dependencies = [ + "bitflags", + "byteorder", + "heed-traits", + "heed-types", + "libc", + "lmdb-master-sys", + "once_cell", + "page_size", + "synchronoise", + "url", +] + +[[package]] +name = "heed-traits" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3130048d404c57ce5a1ac61a903696e8fcde7e8c2991e9fcfc1f27c3ef74ff" + +[[package]] +name = "heed-types" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c255bdf46e07fb840d120a36dcc81f385140d7191c76a7391672675c01a55d" +dependencies = [ + "byteorder", + "heed-traits", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3fef046dca3ca91ee1408a8c1b80ab777e80a4d308d1bf4e7adb3fcb047e08" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lmdb-master-sys" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeb9bd22e73bd1babffff614994b341e9b2008de7bb73bf1f7e9154f1978f8b" +dependencies = [ + "cc", + "doxygen-rs", + "libc", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" + +[[package]] +name = "lru" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9ac18847474e638e3702b76c65d4eb93428471a74778ef0f1be711717f89b5" + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + +[[package]] +name = "mainline" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32eaee3dcba6e0bbbefe8bd896a8bd6039d5e74b199c0fe248e9feb547c2a26" +dependencies = [ + "crc", + "document-features", + "dyn-clone", + "ed25519-dalek", + "flume 0.12.0", + "futures-lite", + "getrandom 0.4.3", + "lru 0.16.4", + "serde", + "serde_bencode", + "serde_bytes", + "sha1_smol", + "thiserror", + "tracing", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntimestamp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50f94c405726d3e0095e89e72f75ce7f6587b94a8bd8dc8054b73f65c0fd68c" +dependencies = [ + "base32", + "document-features", + "getrandom 0.2.17", + "httpdate", + "js-sys", + "once_cell", + "serde", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "paykit-lib" +version = "0.1.0-rc56" +source = "git+https://github.com/pubky/paykit-rs.git?rev=24162ebbcc703251d8038f2e176d1f4cfb117c6a#24162ebbcc703251d8038f2e176d1f4cfb117c6a" +dependencies = [ + "anyhow", + "base64 0.22.1", + "chacha20poly1305", + "chrono", + "pubky", + "pubky-noise", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tracing", + "uuid", + "zeroize", +] + +[[package]] +name = "paykit-sdk" +version = "0.1.0-rc56" +source = "git+https://github.com/pubky/paykit-rs.git?rev=24162ebbcc703251d8038f2e176d1f4cfb117c6a#24162ebbcc703251d8038f2e176d1f4cfb117c6a" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.22.1", + "bip39", + "blake3", + "chrono", + "crypto_secretbox", + "hex", + "paykit-lib", + "pubky", + "reqwest", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror", + "url", + "zeroize", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.8", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkarr" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c043ed867b3152b7b0eb3e0121048c522bfe2e0e8368c7a5e91c042c78a2a5" +dependencies = [ + "async-compat", + "base32", + "byteorder", + "bytes", + "cfg_aliases", + "document-features", + "dyn-clone", + "ed25519-dalek", + "futures-buffered", + "futures-lite", + "genawaiter", + "getrandom 0.4.3", + "heed", + "log", + "lru 0.18.5", + "mainline", + "ntimestamp", + "page_size", + "reqwest", + "rustls", + "rustls-webpki", + "self_cell", + "serde", + "sha1_smol", + "simple-dns", + "thiserror", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "pubky" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a7b191157d57d5095f1577c8064aeda85f96606957fcf4c0a6f4b675adb96d" +dependencies = [ + "async-trait", + "base64 0.22.1", + "cookie", + "eventsource-stream", + "flume 0.11.1", + "futures-lite", + "futures-util", + "httpdate", + "log", + "percent-encoding", + "pkarr", + "pubky-common", + "reqwest", + "rustls", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", + "wasm-bindgen-futures", + "web-time", + "webpki-roots", +] + +[[package]] +name = "pubky-common" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee26f4cf7d9d11a300180b351f1ea0efebf17ec66f0e615a8867f8aba47dc5f" +dependencies = [ + "argon2", + "base64 0.22.1", + "blake3", + "crypto_secretbox", + "ed25519-dalek", + "percent-encoding", + "pkarr", + "postcard", + "pubky-timestamp", + "rand 0.10.3", + "serde", + "serde_json", + "thiserror", + "url", +] + +[[package]] +name = "pubky-noise" +version = "0.1.0-rc8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d506939a2d151814267bc94a1e70f6406d98bda0322da33a014a249edd307eea" +dependencies = [ + "curve25519-dalek 5.0.0", + "ed25519-dalek", + "getrandom 0.3.4", + "hex", + "pubky", + "rand 0.9.5", + "sha2 0.11.0", + "snow", +] + +[[package]] +name = "pubky-timestamp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44aafc63c1eab1905e8a8378d2b085500540c9935b6d028908c8a50a5a246a3d" +dependencies = [ + "base32", + "document-features", + "getrandom 0.2.17", + "httpdate", + "js-sys", + "once_cell", + "serde", +] + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.3", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20 0.10.2", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +dependencies = [ + "base64 0.23.1", + "bytes", + "cookie", + "cookie_store", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_cell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bencode" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e" +dependencies = [ + "serde", + "serde_bytes", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simple-dns" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6f884fa9a8d48101774bfbd3aeb81e968dd22cffd19a372da69f183db22c1a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" + +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.1.3", + "getrandom 0.3.4", + "ring", + "rustc_version", + "sha2 0.10.9", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" + +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synchronoise" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dbc01390fc626ce8d1cffe3376ded2b72a11bb70e1c75f404a210e4daa4def2" +dependencies = [ + "crossbeam-queue", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.6", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df92bf3d9227be3d53173901ddbffac2babc27ae50f397776ffd6dc33f800cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4f328cf2f05d084e496c3e9c3f33ed0a183656a16e1fcec4d464d8373aec82" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/payment-requests/Cargo.toml b/payment-requests/Cargo.toml new file mode 100644 index 0000000..fc2f124 --- /dev/null +++ b/payment-requests/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bitkit-payment-request-fixture" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +async-trait = "0.1" +axum = "0.8" +chrono = "0.4" +paykit-lib = { git = "https://github.com/pubky/paykit-rs.git", rev = "24162ebbcc703251d8038f2e176d1f4cfb117c6a" } +paykit-sdk = { git = "https://github.com/pubky/paykit-rs.git", rev = "24162ebbcc703251d8038f2e176d1f4cfb117c6a" } +pubky = "=0.11.0" +reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "sync"] } diff --git a/payment-requests/Dockerfile b/payment-requests/Dockerfile new file mode 100644 index 0000000..925f781 --- /dev/null +++ b/payment-requests/Dockerfile @@ -0,0 +1,14 @@ +FROM rust:1.98.1-bookworm@sha256:93ce27a88655056a51dbdd8f5f2d7ddc071c7b0070fb288a37b5a285fc83971e AS builder +WORKDIR /usr/src/fixture +COPY Cargo.toml Cargo.lock ./ +COPY src ./src +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=/usr/src/fixture/target \ + cargo build --release --locked && install -Dm755 target/release/bitkit-payment-request-fixture /out/fixture + +FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* +COPY --from=builder /out/fixture /usr/local/bin/fixture +ENTRYPOINT ["/usr/local/bin/fixture"] diff --git a/payment-requests/prepare b/payment-requests/prepare new file mode 100755 index 0000000..0c482da --- /dev/null +++ b/payment-requests/prepare @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# Prepare the deadline-history states between the two disposable rc56 peers. +set -euo pipefail +cd "$(dirname "$0")/.." + +post() { + curl -fsS -X POST "http://127.0.0.1:$1$2" -H 'content-type: application/json' -d "$3" +} + +issuer=$(curl -fsS http://127.0.0.1:3012/info) +peer=$(curl -fsS http://127.0.0.1:3013/info) +to_peer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$peer")" --arg path "$(jq -r .receiver_path <<<"$peer")" '{peer_pubky:$pubky,peer_path:$path}') +to_issuer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$issuer")" --arg path "$(jq -r .receiver_path <<<"$issuer")" '{peer_pubky:$pubky,peer_path:$path}') + +issuer_link=$(post 3012 /sync "$to_peer" 2>/dev/null || true) +peer_link=$(post 3013 /sync "$to_issuer" 2>/dev/null || true) +if ! jq -e '.link == "linked"' <<<"$issuer_link" >/dev/null 2>&1; then + post 3012 /link "$(jq -c '. + {mode:"initiate"}' <<<"$to_peer")" >/dev/null +fi +if ! jq -e '.link == "linked"' <<<"$peer_link" >/dev/null 2>&1; then + post 3013 /link "$(jq -c '. + {mode:"accept"}' <<<"$to_issuer")" >/dev/null +fi +linked=false +for _ in {1..20}; do + issuer_link=$(post 3012 /sync "$to_peer") + peer_link=$(post 3013 /sync "$to_issuer") + if jq -e '.link == "linked"' <<<"$issuer_link" >/dev/null && + jq -e '.link == "linked"' <<<"$peer_link" >/dev/null; then + linked=true + break + fi + sleep 0.2 +done +if [ "$linked" != true ]; then + echo 'Paykit peers did not link' >&2 + exit 1 +fi + +issue() { + local reference="$1" amount="$2" extra="${3:-}" response + if [ -z "$extra" ]; then extra='{}'; fi + response=$(post 3012 /request "$(jq -nc --argjson peer "$to_peer" --argjson extra "$extra" --arg ref "$reference" --argjson sats "$amount" '$peer + $extra + {reference:$ref,amount_sats:$sats}')") + jq -er .payment_request_id <<<"$response" +} + +record() { + jq -nc --argjson peer "$to_issuer" --arg id "$1" '$peer + {payment_request_id:$id}' +} + +receive() { post 3013 /sync "$to_issuer" >/dev/null; } +sync_issuer() { post 3012 /sync "$to_peer" >/dev/null; } + +proposed=$(issue rc56-proposed 10000) +receive + +accepted=$(issue rc56-accepted 11000) +receive +post 3013 /accept "$(record "$accepted")" >/dev/null +sync_issuer + +rejected=$(issue rc56-rejected 12000) +receive +post 3013 /reject "$(record "$rejected")" >/dev/null +sync_issuer + +canceled=$(issue rc56-canceled 13000) +receive +post 3012 /cancel "$(jq -nc --argjson peer "$to_peer" --arg id "$canceled" '$peer + {payment_request_id:$id}')" >/dev/null +receive + +completed=$(issue rc56-completed 14000) +receive +post 3013 /accept "$(record "$completed")" >/dev/null +sync_issuer +paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$completed")" --arg address "$(jq -r .address <<<"$issuer")" '$record + {address:$address,amount_sats:14000}')") +jq -e '.proof_sent == true' <<<"$paid" >/dev/null +sync_issuer + +period=$(python3 - <<'PY' +import calendar +import json +from datetime import datetime, timezone + +now = datetime.now(timezone.utc).replace(microsecond=0) +previous_year = now.year - (now.month == 1) +previous_month = (now.month - 2) % 12 + 1 +start = now.replace(year=previous_year, month=previous_month, + day=min(now.day, calendar.monthrange(previous_year, previous_month)[1])) +end = start.replace(year=now.year, month=now.month, + day=min(start.day, calendar.monthrange(now.year, now.month)[1])) +print(json.dumps({"start": start.isoformat().replace("+00:00", "Z"), "end": end.isoformat().replace("+00:00", "Z")})) +PY +) +monthly_extra=$(jq -nc --arg start "$(jq -r .start <<<"$period")" '{monthly_starts_at:$start,period_start_deadline_seconds:3456000}') +monthly_accepted=$(issue rc56-monthly-accepted 15000 "$monthly_extra") +receive +post 3013 /accept "$(record "$monthly_accepted")" >/dev/null +sync_issuer +monthly_paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$monthly_accepted")" --arg address "$(jq -r .address <<<"$issuer")" --arg start "$(jq -r .start <<<"$period")" --arg end "$(jq -r .end <<<"$period")" '$record + {address:$address,amount_sats:15000,billing_period_start:$start,billing_period_end:$end}')") +jq -e '.proof_sent == true' <<<"$monthly_paid" >/dev/null +sync_issuer +monthly_proposed=$(issue rc56-monthly-proposed 16000 "$monthly_extra") +receive + +./bitcoin-cli mine 1 >/dev/null +for txid in "$(jq -r .txid <<<"$paid")" "$(jq -r .txid <<<"$monthly_paid")"; do + docker compose exec -T bitcoind bitcoin-cli -rpcport=43782 -rpcuser=polaruser -rpcpassword=polarpass gettransaction "$txid" | + jq -e '.confirmations >= 1' >/dev/null +done + +records=$(post 3013 /records "$to_issuer") +check() { + local id="$1" expected="$2" + jq -e --arg id "$id" --arg expected "$expected" '.records[] | select(.payment_request_id == $id and .state == $expected)' <<<"$records" >/dev/null +} +check "$proposed" Proposed +check "$accepted" Accepted +check "$rejected" Rejected +check "$canceled" Canceled +check "$completed" ProofSubmitted +check "$monthly_accepted" ActiveRecurring +check "$monthly_proposed" Proposed +jq -e --arg id "$monthly_accepted" '.records[] | select(.payment_request_id == $id and (.payment_proofs | length) == 1)' <<<"$records" >/dev/null + +jq -nc --arg proposed "$proposed" --arg accepted "$accepted" --arg rejected "$rejected" \ + --arg canceled "$canceled" --arg completed "$completed" --arg monthly_accepted "$monthly_accepted" \ + --arg monthly_proposed "$monthly_proposed" --arg unpaid_start "$(jq -r .end <<<"$period")" \ + --arg completed_txid "$(jq -r .txid <<<"$paid")" \ + --arg monthly_txid "$(jq -r .txid <<<"$monthly_paid")" \ + '{one_time:{proposed:$proposed,accepted:$accepted,rejected:$rejected,canceled:$canceled,proof_submitted:$completed},monthly:{accepted:$monthly_accepted,proposed:$monthly_proposed,paid_period_txid:$monthly_txid,unpaid_period_start:$unpaid_start},completed_txid:$completed_txid,verified:true}' diff --git a/payment-requests/src/main.rs b/payment-requests/src/main.rs new file mode 100644 index 0000000..86e855f --- /dev/null +++ b/payment-requests/src/main.rs @@ -0,0 +1,530 @@ +//! Disposable rc56 Paykit peer for Bitkit regtest journeys. +//! Two Compose services run this binary with separate identities and receiver paths. +#![recursion_limit = "512"] + +use std::{env, sync::Arc}; + +use anyhow::{anyhow, bail, Context, Result}; +use async_trait::async_trait; +use axum::{ + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, + routing::{get, post}, + Json, Router, +}; +use chrono::{Duration, SecondsFormat, Utc}; +use paykit_lib::{ + BillingPeriod, PaymentAmount, PaymentDeadline, PaymentEndpointIdentifier, PaymentReference, + PaymentRequestId, PaymentRequestTerms, Recurrence, RecurrenceConfig, RecurrenceUnit, +}; +use paykit_sdk::{ + InMemoryStorage, LinkedPeerState, PaykitReceiverCapabilities, PaykitReceiverPath, PaykitSdk, + PaykitSdkConfig, PaymentAdapter, PaymentRequestLifecycleState, PubkyLocalSecretKey, + PubkyPublicKey, PubkySessionAccess, PubkySessionBootstrap, PubkySessionProvider, + ReceiverNoiseSecretKey, +}; +use pubky::{Keypair, Pubky}; +use serde::Deserialize; +use serde_json::{json, Value}; +use tokio::sync::Mutex; + +const HOMESERVER: &str = "pubky8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"; +const ENDPOINT: &str = "btc-regtest-p2wpkh"; + +#[derive(Clone)] +struct SessionProvider(Arc>>); + +#[async_trait] +impl PubkySessionProvider for SessionProvider { + async fn load_session_access(&self) -> paykit_sdk::Result> { + Ok(self.0.lock().await.clone()) + } + + async fn load_public_storage(&self) -> paykit_sdk::Result> { + Ok(self + .0 + .lock() + .await + .as_ref() + .map(|access| access.outbox_client.public_storage())) + } + + async fn clear_session_access(&self) -> paykit_sdk::Result<()> { + *self.0.lock().await = None; + Ok(()) + } +} + +struct FixturePaymentAdapter; +#[async_trait] +impl PaymentAdapter for FixturePaymentAdapter {} + +type FixtureSdk = PaykitSdk; + +struct App { + sdk: FixtureSdk, + pubky: PubkyPublicKey, + receiver_path: PaykitReceiverPath, + address: String, + role: String, + // The SDK stores per-peer operation leases; serialize manual control calls. + operation: Mutex<()>, +} + +#[derive(Deserialize)] +struct Peer { + peer_pubky: String, + peer_path: String, +} + +impl Peer { + fn parsed(&self) -> Result<(PubkyPublicKey, PaykitReceiverPath)> { + Ok(( + PubkyPublicKey::from_raw_or_app_key(&self.peer_pubky)?, + PaykitReceiverPath::new(&self.peer_path)?, + )) + } +} + +#[derive(Deserialize)] +struct LinkInput { + #[serde(flatten)] + peer: Peer, + mode: String, +} + +#[derive(Deserialize)] +struct RequestInput { + #[serde(flatten)] + peer: Peer, + amount_sats: u64, + reference: String, + deadline_at: Option, + monthly_starts_at: Option, + period_start_deadline_seconds: Option, +} + +#[derive(Deserialize)] +struct RecordInput { + #[serde(flatten)] + peer: Peer, + payment_request_id: String, +} + +#[derive(Deserialize)] +struct PayInput { + #[serde(flatten)] + record: RecordInput, + address: String, + amount_sats: u64, + txid: Option, + billing_period_start: Option, + billing_period_end: Option, +} + +struct ApiError(anyhow::Error); +impl> From for ApiError { + fn from(error: E) -> Self { + Self(error.into()) + } +} +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + ( + StatusCode::BAD_REQUEST, + Json(json!({ "error": self.0.to_string() })), + ) + .into_response() + } +} +type ApiResult = std::result::Result, ApiError>; + +async fn rpc(method: &str, params: Value) -> Result { + let url = env::var("BITCOIN_RPC_URL").unwrap_or_else(|_| "http://bitcoind:43782".into()); + let response: Value = reqwest::Client::new() + .post(url) + .basic_auth("polaruser", Some("polarpass")) + .json(&json!({ "jsonrpc": "1.0", "id": "payment-fixture", "method": method, "params": params })) + .send() + .await? + .json() + .await?; + if !response["error"].is_null() { + bail!("bitcoind {method}: {}", response["error"]); + } + Ok(response["result"].clone()) +} + +async fn setup() -> Result { + let role = env::var("FIXTURE_ROLE").context("FIXTURE_ROLE is required")?; + let receiver_path = PaykitReceiverPath::new(match role.as_str() { + "fixture-issuer" => "bitkit/server", + "rc56-peer" => "bitkit/wallet", + _ => bail!("unknown FIXTURE_ROLE"), + })?; + let pubky = Pubky::testnet()?; + let bootstrap = PubkySessionBootstrap::with_pubky(pubky, "bitkit-docker.fixture")? + .with_auth_relay("http://localhost:15412/inbox")?; + let config = PaykitSdkConfig::new(receiver_path.clone()); + let secret = PubkyLocalSecretKey::new(Keypair::random().secret_key()); + let homeserver = PubkyPublicKey::from_raw_or_app_key(HOMESERVER)?; + let signed_up = bootstrap + .sign_up( + &secret, + ReceiverNoiseSecretKey::random(), + &homeserver, + None, + &config.required_session_capabilities(), + ) + .await?; + let provider = SessionProvider(Arc::new(Mutex::new(Some(signed_up.access)))); + let sdk = PaykitSdk::new( + InMemoryStorage::default(), + provider, + FixturePaymentAdapter, + config, + )?; + sdk.initialize().await?; + sdk.publish_paykit_receiver_marker(PaykitReceiverCapabilities { + private_payments: true, + payment_requests: true, + receipts: false, + outgoing_payments: role == "rc56-peer", + }) + .await?; + let address = rpc("getnewaddress", json!(["", "bech32"])) + .await? + .as_str() + .context("bitcoind returned no address")? + .to_owned(); + if !address.starts_with("bcrt1q") { + bail!("bitcoind did not return a regtest bech32 address"); + } + let endpoint_payload = json!({ "value": address }).to_string(); + let published = sdk + .sync_public_endpoints_with_receiving_details(vec![paykit_sdk::PublicReceivingDetail { + identifier: ENDPOINT.into(), + payload: endpoint_payload, + }]) + .await?; + if !published.failed.is_empty() || published.published.len() != 1 { + bail!("Paykit endpoint publication failed"); + } + Ok(App { + sdk, + pubky: signed_up.public_key, + receiver_path, + address, + role, + operation: Mutex::new(()), + }) +} + +async fn info(State(app): State>) -> ApiResult { + Ok(Json(json!({ + "status": "ready", "role": app.role, "pubky": app.pubky.to_app_key(), + "receiver_path": app.receiver_path.as_str(), "endpoint": ENDPOINT, + "address": app.address, + }))) +} + +async fn link(State(app): State>, Json(input): Json) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.peer.parsed()?; + let report = match input.mode.as_str() { + "initiate" => app.sdk.initiate_link_with_peer(peer, path).await?, + "accept" => app.sdk.accept_link_with_peer(peer, path).await?, + _ => return Err(anyhow!("mode must be initiate or accept").into()), + }; + Ok(Json(serde_json::to_value(report)?)) +} + +async fn sync_locked(app: &App, peer: PubkyPublicKey, path: PaykitReceiverPath) -> Result { + let current = app + .sdk + .linked_peers() + .await? + .into_iter() + .find(|item| item.counterparty == peer && item.counterparty_receiver_path == path); + let state = current.context("link not started")?.state; + if state == LinkedPeerState::Linking { + let report = app.sdk.advance_link_handshake(peer, path).await?; + return Ok(json!({ "link": report })); + } + if state != LinkedPeerState::Linked { + bail!("peer link is {state:?}"); + } + let received = app + .sdk + .receive_private_messages(peer.clone(), path.clone()) + .await?; + let sent = app + .sdk + .process_outbound_private_messages(peer.clone(), path.clone()) + .await?; + let records = app.sdk.payment_requests_with(&peer, &path).await?; + Ok( + json!({ "link": "linked", "received": received.stream_item_ids.len(), + "sent": sent.sent.len(), "failed": sent.failed.len(), "records": records }), + ) +} + +async fn sync(State(app): State>, Json(input): Json) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.parsed()?; + Ok(Json(sync_locked(&app, peer, path).await?)) +} + +async fn issue(State(app): State>, Json(input): Json) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.peer.parsed()?; + if input.amount_sats == 0 || input.reference.is_empty() { + return Err(anyhow!("amount_sats and reference are required").into()); + } + let now = Utc::now(); + let terms = PaymentRequestTerms::builder( + PaymentAmount::new( + format!( + "{}.{:08}", + input.amount_sats / 100_000_000, + input.amount_sats % 100_000_000 + ), + "btc", + )?, + PaymentReference::new(input.reference)?, + vec![PaymentEndpointIdentifier::new(ENDPOINT)?], + ); + let terms = if let Some(start) = input.monthly_starts_at { + let recurrence = Recurrence::try_from(RecurrenceConfig { + every: 1, + unit: RecurrenceUnit::Month, + starts_at: start.clone(), + anchor: start, + ends_at: None, + })?; + terms + .recurrence(Some(recurrence)) + .payment_deadline(Some(PaymentDeadline::PeriodStart { + seconds: input.period_start_deadline_seconds.unwrap_or(86_400), + })) + } else { + let deadline = input.deadline_at.unwrap_or_else(|| { + (now + Duration::days(7)).to_rfc3339_opts(SecondsFormat::Secs, true) + }); + terms.payment_deadline(Some(PaymentDeadline::At { + timestamp: deadline, + })) + }; + let record = app + .sdk + .propose_payment_request(peer.clone(), path.clone(), terms.build()?) + .await?; + let sent = app + .sdk + .process_outbound_private_messages(peer, path) + .await?; + if !sent.failed.is_empty() || sent.sent.len() != 1 { + return Err(anyhow!( + "Payment Request delivery failed: {} failed, {} sent", + sent.failed.len(), + sent.sent.len() + ) + .into()); + } + Ok(Json( + json!({ "payment_request_id": record.payment_request_id, "state": record.state, + "deadline": record.terms.as_ref().and_then(|terms| terms.payment_deadline.as_ref()) }), + )) +} + +async fn records(State(app): State>, Json(input): Json) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.parsed()?; + Ok(Json( + json!({ "records": app.sdk.payment_requests_with(&peer, &path).await? }), + )) +} + +async fn act(State(app): State>, action: &'static str, input: RecordInput) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.peer.parsed()?; + let _ = app + .sdk + .receive_private_messages(peer.clone(), path.clone()) + .await?; + let id = PaymentRequestId::new(input.payment_request_id)?; + let record = match action { + "accept" => { + app.sdk + .accept_payment_request(peer.clone(), path.clone(), &id) + .await? + } + "reject" => { + app.sdk + .reject_payment_request(peer.clone(), path.clone(), &id, None) + .await? + } + "cancel" => { + app.sdk + .cancel_payment_request(peer.clone(), path.clone(), &id, None) + .await? + } + _ => unreachable!(), + }; + let sent = app + .sdk + .process_outbound_private_messages(peer, path) + .await?; + if !sent.failed.is_empty() || sent.sent.len() != 1 { + return Err(anyhow!("{action} delivery failed").into()); + } + Ok(Json( + json!({ "payment_request_id": record.payment_request_id, "state": record.state }), + )) +} + +async fn accept(State(app): State>, Json(input): Json) -> ApiResult { + act(State(app), "accept", input).await +} +async fn reject(State(app): State>, Json(input): Json) -> ApiResult { + act(State(app), "reject", input).await +} +async fn cancel(State(app): State>, Json(input): Json) -> ApiResult { + act(State(app), "cancel", input).await +} + +async fn pay_impl(app: Arc, input: PayInput, existing_tx: bool) -> ApiResult { + let _guard = app.operation.lock().await; + let (peer, path) = input.record.peer.parsed()?; + let id = PaymentRequestId::new(input.record.payment_request_id)?; + let _ = app + .sdk + .receive_private_messages(peer.clone(), path.clone()) + .await?; + let record = app + .sdk + .payment_requests_with(&peer, &path) + .await? + .into_iter() + .find(|record| record.payment_request_id == id.as_str()) + .context("unknown Payment Request")?; + if !matches!( + record.state, + PaymentRequestLifecycleState::Accepted | PaymentRequestLifecycleState::ActiveRecurring + ) { + return Err(anyhow!("Payment Request is not accepted").into()); + } + if input.amount_sats == 0 || !input.address.starts_with("bcrt1") { + return Err(anyhow!("positive amount_sats and regtest bech32 address required").into()); + } + let amount = format!( + "{}.{:08}", + input.amount_sats / 100_000_000, + input.amount_sats % 100_000_000 + ); + let terms = record.terms.context("Payment Request has no terms")?; + if terms.amount.asset != "btc" + || terms.amount.value.parse::().ok() != amount.parse::().ok() + { + return Err(anyhow!("payment amount does not match BTC request").into()); + } + let period = match (input.billing_period_start, input.billing_period_end) { + (Some(start), Some(end)) => Some(BillingPeriod::new(start, end)?), + (None, None) => None, + _ => return Err(anyhow!("both billing period bounds are required").into()), + }; + if terms.recurrence.is_some() != period.is_some() { + return Err(anyhow!("billing period required only for monthly requests").into()); + } + let txid = if existing_tx { + let txid = input.txid.context("txid required for /proof")?; + let transaction = rpc("gettransaction", json!([txid])).await?; + let matches_payment = transaction["details"].as_array().is_some_and(|details| { + details.iter().any(|detail| { + detail["category"] == "send" + && detail["address"] == input.address + && detail["amount"].as_f64().is_some_and(|value| { + (value.abs() - input.amount_sats as f64 / 100_000_000.0).abs() < 0.00000001 + }) + }) + }); + if !matches_payment { + return Err(anyhow!("txid has no matching regtest wallet payment").into()); + } + txid + } else { + if input.txid.is_some() { + return Err(anyhow!("use /proof to retry an existing transaction").into()); + } + rpc( + "sendtoaddress", + json!([input.address, input.amount_sats as f64 / 100_000_000.0]), + ) + .await? + .as_str() + .context("bitcoind returned no txid")? + .to_owned() + }; + let proof = json!({ "txid": txid }).as_object().unwrap().clone(); + let record = match app + .sdk + .submit_payment_proof( + peer.clone(), + path.clone(), + &id, + period, + PaymentEndpointIdentifier::new(ENDPOINT)?, + proof, + ) + .await + { + Ok(record) => record, + Err(error) => { + return Ok(Json( + json!({ "payment_request_id": id.as_str(), "txid": txid, + "proof_sent": false, "error": error.to_string() }), + )) + } + }; + let sent = app.sdk.process_outbound_private_messages(peer, path).await; + let proof_sent = sent + .as_ref() + .is_ok_and(|report| report.failed.is_empty() && report.sent.len() == 1); + Ok(Json( + json!({ "payment_request_id": id.as_str(), "txid": txid, + "state": record.state, "proof_sent": proof_sent }), + )) +} + +async fn pay(State(app): State>, Json(input): Json) -> ApiResult { + pay_impl(app, input, false).await +} + +async fn proof(State(app): State>, Json(input): Json) -> ApiResult { + pay_impl(app, input, true).await +} + +#[tokio::main] +async fn main() -> Result<()> { + let app = Arc::new(setup().await?); + let port: u16 = env::var("FIXTURE_PORT") + .unwrap_or_else(|_| "3002".into()) + .parse()?; + let router = Router::new() + .route("/health", get(info)) + .route("/info", get(info)) + .route("/link", post(link)) + .route("/sync", post(sync)) + .route("/records", post(records)) + .route("/request", post(issue)) + .route("/accept", post(accept)) + .route("/reject", post(reject)) + .route("/cancel", post(cancel)) + .route("/pay", post(pay)) + .route("/proof", post(proof)) + .with_state(app); + let listener = tokio::net::TcpListener::bind(("0.0.0.0", port)).await?; + axum::serve(listener, router).await?; + Ok(()) +} From add38169493513320d2aade78b519eeb1719a021 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 29 Sep 2026 21:34:22 +0200 Subject: [PATCH 2/3] docs: list payment request fixture ports and namespace in the marketplace docs --- docs/pubky-marketplace.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pubky-marketplace.md b/docs/pubky-marketplace.md index 07e4af5..175cfb2 100644 --- a/docs/pubky-marketplace.md +++ b/docs/pubky-marketplace.md @@ -55,11 +55,14 @@ these fixed ports: | 15411, 15412 | PKARR relay, HTTP relay | | 6881 (tcp and udp) | DHT bootstrap | | 3001 | Paykit Server (`MARKETPLACE_PAYKIT_PORT`) | +| 3012, 3013 | `fixture-issuer` and `rc56-peer` of the opt-in `payment-requests` profile (see the README); nothing listens until they run | | 16288 | homeserver admin (`MARKETPLACE_HOMESERVER_ADMIN_PORT`; the in-container 6288 is Homegate's host port) | | 60001 | Electrum, from the base stack | `paykit-server` and `marketplace-driver` share the `pubky-testnet` network namespace, as the upstream -Locks compose does, so their Pubky clients reach the testnet on localhost. +Locks compose does, so their Pubky clients reach the testnet on localhost. The `payment-requests` profile's `fixture-issuer` +and `rc56-peer` join it too. They sign up on the testnet homeserver and use its HTTP relay, and do not talk +to Paykit Server, so its pin does not affect them. ## Roles From 067988d891281e44c18ba735da7247b09613c380 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 29 Sep 2026 23:01:42 +0200 Subject: [PATCH 3/3] fix: retry the payment request peers' setup, surface prepare errors and remove the peers with the fixture --- README.md | 21 +++++-- payment-requests/Cargo.toml | 2 +- payment-requests/prepare | 57 ++++++++++++++----- payment-requests/src/main.rs | 107 ++++++++++++++++++++++++----------- pubky-marketplace | 12 ++-- 5 files changed, 142 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 587535c..8036b71 100644 --- a/README.md +++ b/README.md @@ -225,10 +225,18 @@ cancel and pay requests through the shared regtest Bitcoin node. Plain ./pubky-marketplace seed docker compose --profile marketplace --profile payment-requests build fixture-issuer docker compose --profile marketplace --profile payment-requests up -d --no-build fixture-issuer rc56-peer -curl -fsS http://127.0.0.1:3012/health | jq -curl -fsS http://127.0.0.1:3013/health | jq +for port in 3012 3013; do + until health=$(curl -fsS "http://127.0.0.1:$port/health"); do sleep 2; done + jq <<<"$health" +done ``` +The peers sign up on the testnet and publish their endpoints before they listen, +so `/health` fails for a few seconds after `up`. The loop waits for them, and +`payment-requests/prepare` does the same for up to 150 seconds. A peer retries +its setup for two minutes and then exits; if the loop does not end, stop it and +read `docker compose --profile marketplace --profile payment-requests logs fixture-issuer rc56-peer`. + Each `/health` response gives the identity, receiver path and published `btc-regtest-p2wpkh` address. Prepare and verify all one-time J1 states plus an accepted monthly subscription with one paid period and a new monthly @@ -264,10 +272,11 @@ curl -fsS -X POST http://127.0.0.1:3012/request -H 'content-type: application/js -d "$(jq -nc --arg pubky "$APP_PUBKY" '{peer_pubky:$pubky,peer_path:"bitkit/wallet",amount_sats:15000,reference:"rc56-app-history"}')" | jq ``` -These peers keep their identities and SDK records in memory. After a restart, -remove `fixture-issuer` and `rc56-peer` with -`docker compose --profile marketplace --profile payment-requests rm -sf fixture-issuer rc56-peer`, -then run `./pubky-marketplace reset` and relink the app. `./pubky-marketplace seed` +These peers keep their identities and SDK records in memory and live in the +Pubky testnet's network namespace, so `./pubky-marketplace down` and `reset` +remove them together with the testnet. After `reset`, start them again with the +`up -d --no-build fixture-issuer rc56-peer` command above, wait for `/health`, +rerun `payment-requests/prepare` and relink the app. `./pubky-marketplace seed` needs outbound internet for Paykit Server setup; the rc56 peer calls use the local testnet. The lane still needs a Bitkit build pointed at the local Pubky testnet and to verify the requested rows on device. The headless preparation diff --git a/payment-requests/Cargo.toml b/payment-requests/Cargo.toml index fc2f124..1b5d38e 100644 --- a/payment-requests/Cargo.toml +++ b/payment-requests/Cargo.toml @@ -14,4 +14,4 @@ pubky = "=0.11.0" reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "sync"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "sync", "time"] } diff --git a/payment-requests/prepare b/payment-requests/prepare index 0c482da..b9813e1 100755 --- a/payment-requests/prepare +++ b/payment-requests/prepare @@ -3,22 +3,47 @@ set -euo pipefail cd "$(dirname "$0")/.." +fail() { + echo "prepare: $*" >&2 + exit 1 +} + +# --fail-with-body keeps the peer's JSON error; show it with the failing call instead of a bare HTTP code. post() { - curl -fsS -X POST "http://127.0.0.1:$1$2" -H 'content-type: application/json' -d "$3" + local response + if ! response=$(curl --fail-with-body -sS -X POST "http://127.0.0.1:$1$2" -H 'content-type: application/json' -d "$3" 2>&1); then + echo "prepare: POST :$1$2 failed: $response" >&2 + return 1 + fi + printf '%s\n' "$response" +} + +# The peers publish their identities before they listen. Until then Docker's port proxy refuses or resets the +# connection (curl --retry retries only the refusal), so poll /health for up to 150 seconds. +wait_health() { + until curl -fsS -o /dev/null "http://127.0.0.1:$1/health" 2>/dev/null; do + if [ "$SECONDS" -ge "$deadline" ]; then + fail "port $1 did not answer /health; see: docker compose --profile marketplace --profile payment-requests logs fixture-issuer rc56-peer" + fi + sleep 2 + done } +deadline=$((SECONDS + 150)) +wait_health 3012 +wait_health 3013 -issuer=$(curl -fsS http://127.0.0.1:3012/info) -peer=$(curl -fsS http://127.0.0.1:3013/info) +issuer=$(curl --fail-with-body -sS http://127.0.0.1:3012/info) +peer=$(curl --fail-with-body -sS http://127.0.0.1:3013/info) to_peer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$peer")" --arg path "$(jq -r .receiver_path <<<"$peer")" '{peer_pubky:$pubky,peer_path:$path}') to_issuer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$issuer")" --arg path "$(jq -r .receiver_path <<<"$issuer")" '{peer_pubky:$pubky,peer_path:$path}') issuer_link=$(post 3012 /sync "$to_peer" 2>/dev/null || true) peer_link=$(post 3013 /sync "$to_issuer" 2>/dev/null || true) if ! jq -e '.link == "linked"' <<<"$issuer_link" >/dev/null 2>&1; then - post 3012 /link "$(jq -c '. + {mode:"initiate"}' <<<"$to_peer")" >/dev/null + post 3012 /link "$(jq -c '. + {mode:"initiate"}' <<<"$to_peer")" >/dev/null || fail 'issuer link initiate failed' fi if ! jq -e '.link == "linked"' <<<"$peer_link" >/dev/null 2>&1; then - post 3013 /link "$(jq -c '. + {mode:"accept"}' <<<"$to_issuer")" >/dev/null + post 3013 /link "$(jq -c '. + {mode:"accept"}' <<<"$to_issuer")" >/dev/null || fail 'peer link accept failed' fi linked=false for _ in {1..20}; do @@ -39,8 +64,11 @@ fi issue() { local reference="$1" amount="$2" extra="${3:-}" response if [ -z "$extra" ]; then extra='{}'; fi - response=$(post 3012 /request "$(jq -nc --argjson peer "$to_peer" --argjson extra "$extra" --arg ref "$reference" --argjson sats "$amount" '$peer + $extra + {reference:$ref,amount_sats:$sats}')") - jq -er .payment_request_id <<<"$response" + response=$(post 3012 /request "$(jq -nc --argjson peer "$to_peer" --argjson extra "$extra" --arg ref "$reference" --argjson sats "$amount" '$peer + $extra + {reference:$ref,amount_sats:$sats}')") || return 1 + jq -er .payment_request_id <<<"$response" || { + echo "prepare: no payment_request_id issuing $reference: $response" >&2 + return 1 + } } record() { @@ -73,7 +101,7 @@ receive post 3013 /accept "$(record "$completed")" >/dev/null sync_issuer paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$completed")" --arg address "$(jq -r .address <<<"$issuer")" '$record + {address:$address,amount_sats:14000}')") -jq -e '.proof_sent == true' <<<"$paid" >/dev/null +jq -e '.proof_sent == true' <<<"$paid" >/dev/null || fail "request $completed: /pay did not deliver the proof: $paid" sync_issuer period=$(python3 - <<'PY' @@ -97,7 +125,7 @@ receive post 3013 /accept "$(record "$monthly_accepted")" >/dev/null sync_issuer monthly_paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$monthly_accepted")" --arg address "$(jq -r .address <<<"$issuer")" --arg start "$(jq -r .start <<<"$period")" --arg end "$(jq -r .end <<<"$period")" '$record + {address:$address,amount_sats:15000,billing_period_start:$start,billing_period_end:$end}')") -jq -e '.proof_sent == true' <<<"$monthly_paid" >/dev/null +jq -e '.proof_sent == true' <<<"$monthly_paid" >/dev/null || fail "request $monthly_accepted: /pay did not deliver the proof: $monthly_paid" sync_issuer monthly_proposed=$(issue rc56-monthly-proposed 16000 "$monthly_extra") receive @@ -105,13 +133,15 @@ receive ./bitcoin-cli mine 1 >/dev/null for txid in "$(jq -r .txid <<<"$paid")" "$(jq -r .txid <<<"$monthly_paid")"; do docker compose exec -T bitcoind bitcoin-cli -rpcport=43782 -rpcuser=polaruser -rpcpassword=polarpass gettransaction "$txid" | - jq -e '.confirmations >= 1' >/dev/null + jq -e '.confirmations >= 1' >/dev/null || fail "transaction $txid is not confirmed after mining one block" done records=$(post 3013 /records "$to_issuer") check() { - local id="$1" expected="$2" - jq -e --arg id "$id" --arg expected "$expected" '.records[] | select(.payment_request_id == $id and .state == $expected)' <<<"$records" >/dev/null + local id="$1" expected="$2" actual + jq -e --arg id "$id" --arg expected "$expected" '.records[] | select(.payment_request_id == $id and .state == $expected)' <<<"$records" >/dev/null && return 0 + actual=$(jq -r --arg id "$id" '[.records[] | select(.payment_request_id == $id) | .state] | first // "missing"' <<<"$records") + fail "request $id is $actual, expected $expected" } check "$proposed" Proposed check "$accepted" Accepted @@ -120,7 +150,8 @@ check "$canceled" Canceled check "$completed" ProofSubmitted check "$monthly_accepted" ActiveRecurring check "$monthly_proposed" Proposed -jq -e --arg id "$monthly_accepted" '.records[] | select(.payment_request_id == $id and (.payment_proofs | length) == 1)' <<<"$records" >/dev/null +jq -e --arg id "$monthly_accepted" '.records[] | select(.payment_request_id == $id and (.payment_proofs | length) == 1)' <<<"$records" >/dev/null || + fail "request $monthly_accepted does not hold exactly one payment proof" jq -nc --arg proposed "$proposed" --arg accepted "$accepted" --arg rejected "$rejected" \ --arg canceled "$canceled" --arg completed "$completed" --arg monthly_accepted "$monthly_accepted" \ diff --git a/payment-requests/src/main.rs b/payment-requests/src/main.rs index 86e855f..9a87693 100644 --- a/payment-requests/src/main.rs +++ b/payment-requests/src/main.rs @@ -2,7 +2,7 @@ //! Two Compose services run this binary with separate identities and receiver paths. #![recursion_limit = "512"] -use std::{env, sync::Arc}; +use std::{env, future::Future, sync::Arc, time::Instant}; use anyhow::{anyhow, bail, Context, Result}; use async_trait::async_trait; @@ -156,6 +156,31 @@ async fn rpc(method: &str, params: Value) -> Result { Ok(response["result"].clone()) } +/// Retries a step that depends on the Pubky testnet or bitcoind, which may still be starting when this +/// container starts (Compose only waits for their containers to exist). Bounded by +/// `FIXTURE_SETUP_TIMEOUT_SECONDS` (default 120), so a broken dependency still ends in a clear exit. +async fn retry(what: &str, mut attempt: F) -> Result +where + F: FnMut() -> Fut, + Fut: Future>, +{ + let timeout: u64 = env::var("FIXTURE_SETUP_TIMEOUT_SECONDS") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(120); + let deadline = Instant::now() + std::time::Duration::from_secs(timeout); + loop { + match attempt().await { + Ok(value) => return Ok(value), + Err(error) if Instant::now() < deadline => { + eprintln!("setup: {what} failed, retrying: {error:#}"); + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + } + Err(error) => return Err(error.context(format!("{what} still failing after {timeout}s"))), + } + } +} + async fn setup() -> Result { let role = env::var("FIXTURE_ROLE").context("FIXTURE_ROLE is required")?; let receiver_path = PaykitReceiverPath::new(match role.as_str() { @@ -167,17 +192,21 @@ async fn setup() -> Result { let bootstrap = PubkySessionBootstrap::with_pubky(pubky, "bitkit-docker.fixture")? .with_auth_relay("http://localhost:15412/inbox")?; let config = PaykitSdkConfig::new(receiver_path.clone()); - let secret = PubkyLocalSecretKey::new(Keypair::random().secret_key()); let homeserver = PubkyPublicKey::from_raw_or_app_key(HOMESERVER)?; - let signed_up = bootstrap - .sign_up( - &secret, - ReceiverNoiseSecretKey::random(), - &homeserver, - None, - &config.required_session_capabilities(), - ) - .await?; + // A new identity per attempt: a failed sign-up must not leave the retry with a half-created account. + let signed_up = retry("sign-up on the local homeserver", || async { + let secret = PubkyLocalSecretKey::new(Keypair::random().secret_key()); + Ok(bootstrap + .sign_up( + &secret, + ReceiverNoiseSecretKey::random(), + &homeserver, + None, + &config.required_session_capabilities(), + ) + .await?) + }) + .await?; let provider = SessionProvider(Arc::new(Mutex::new(Some(signed_up.access)))); let sdk = PaykitSdk::new( InMemoryStorage::default(), @@ -186,31 +215,43 @@ async fn setup() -> Result { config, )?; sdk.initialize().await?; - sdk.publish_paykit_receiver_marker(PaykitReceiverCapabilities { - private_payments: true, - payment_requests: true, - receipts: false, - outgoing_payments: role == "rc56-peer", + retry("receiver marker publication", || async { + Ok(sdk + .publish_paykit_receiver_marker(PaykitReceiverCapabilities { + private_payments: true, + payment_requests: true, + receipts: false, + outgoing_payments: role == "rc56-peer", + }) + .await?) + }) + .await?; + let address = retry("getnewaddress from bitcoind", || async { + let address = rpc("getnewaddress", json!(["", "bech32"])) + .await? + .as_str() + .context("bitcoind returned no address")? + .to_owned(); + if !address.starts_with("bcrt1q") { + bail!("bitcoind did not return a regtest bech32 address"); + } + Ok(address) }) .await?; - let address = rpc("getnewaddress", json!(["", "bech32"])) - .await? - .as_str() - .context("bitcoind returned no address")? - .to_owned(); - if !address.starts_with("bcrt1q") { - bail!("bitcoind did not return a regtest bech32 address"); - } let endpoint_payload = json!({ "value": address }).to_string(); - let published = sdk - .sync_public_endpoints_with_receiving_details(vec![paykit_sdk::PublicReceivingDetail { - identifier: ENDPOINT.into(), - payload: endpoint_payload, - }]) - .await?; - if !published.failed.is_empty() || published.published.len() != 1 { - bail!("Paykit endpoint publication failed"); - } + retry("Paykit endpoint publication", || async { + let published = sdk + .sync_public_endpoints_with_receiving_details(vec![paykit_sdk::PublicReceivingDetail { + identifier: ENDPOINT.into(), + payload: endpoint_payload.clone(), + }]) + .await?; + if !published.failed.is_empty() || published.published.len() != 1 { + bail!("Paykit endpoint publication failed"); + } + Ok(()) + }) + .await?; Ok(App { sdk, pubky: signed_up.public_key, diff --git a/pubky-marketplace b/pubky-marketplace index c2733e3..150bdfe 100755 --- a/pubky-marketplace +++ b/pubky-marketplace @@ -21,6 +21,8 @@ PAYKIT_PORT="${MARKETPLACE_PAYKIT_PORT:-3001}" COMPOSE=(docker compose --profile marketplace) CHAIN_SERVICES=(bitcoind bitcoinsetup electrs) FIXTURE_SERVICES=(marketplace-postgres pubky-testnet paykit-server) +# Opt-in payment request peers (README, Payment Request fixture). They share the testnet's network namespace. +PEER_SERVICES=(fixture-issuer rc56-peer) show_help() { cat </dev/null progress "removing the evidence"