diff --git a/docker/maxplayer-cashu-sandbox/Dockerfile b/docker/maxplayer-cashu-sandbox/Dockerfile new file mode 100644 index 000000000..c21c6f45f --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/Dockerfile @@ -0,0 +1,251 @@ +# syntax=docker/dockerfile:1 +# +# The Cashu/CDK specialist job sandbox. +# +# It is the ordinary maxplayer sandbox image plus what a Cashu/CDK job cannot get at run time +# without paying for it: +# +# * a PINNED Rust toolchain and a warm CDK dependency cache, so a job compiles CDK code OFFLINE +# (advisor F1 — the first cut shipped only precompiled binaries, and BuildKit cache mounts +# never become image layers); +# * the Cashu spec and CDK source at pinned commits; +# * the curated knowledge topics AT A CONTAINER PATH (advisor F2 — the injected MEMORY.md index +# used to link host files that a contained job cannot reach); +# * `cdk-mintd` built at the same CDK version this workspace pins, plus a start/stop/reset rig for +# a loopback-only fakewallet test mint whose private state stays OUT of the delivered workdir +# (advisor F3); +# * a baked acceptance harness and a from-source example. +# +# The mint here is a FAKEWALLET mint: worthless test ecash, 127.0.0.1 only, state disposable. It +# must never appear in a seller's `accepted_mints` — checked twice in +# crates/maxplayer-core/src/payment_wallet.rs (realized mint, and the NUT-18 payload mint). Nothing +# in this image writes to a seller config. +# +# Build from the REPO ROOT, after the base image exists: +# docker buildx build -f docker/maxplayer-sandbox/Dockerfile -t maxplayer-sandbox:v0.5.8-local . +# docker buildx build -f docker/maxplayer-cashu-sandbox/Dockerfile \ +# --build-arg BASE_IMAGE=maxplayer-sandbox:v0.5.8-local \ +# -t maxplayer-cashu-sandbox:v0.5.8-local . +# +# Every FROM below is pinned by DIGEST (advisor F6): `rust:1-bookworm` and `debian:bookworm-slim` +# are moving tags, and a rebuild against a different toolchain is a different artifact. BASE_IMAGE +# defaults to the digest of the base built from this same commit; pass a tag only deliberately. + +ARG BASE_IMAGE=maxplayer-sandbox@sha256:4b644531ffe3fdc0c4414aa8d6f9027c58a3f18124bb7abe61bb622973731eea +# rust:1-bookworm as of 2026-09-08 = rustc 1.98.1 (48a229cea 2026-09-01), cargo 1.98.1. +ARG RUST_IMAGE=rust@sha256:9a73a5088750b4c95158ab26629c854c3d6fc4b173cb7bc8079ad252d8ed7bfa +ARG DEBIAN_IMAGE=debian@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 + +# CDK version is pinned to the version THIS workspace depends on +# (crates/maxplayer-core/Cargo.toml:62,83-84 and crates/maxplayer/Cargo.toml:105-107 — all =0.17.2). +# A test mint on a different CDK than the wallet under test would make a protocol mismatch look +# like a bug in our code. +ARG CDK_VERSION=0.17.2 +# cashubtc/cdk v0.17.2 tag object. +ARG CDK_COMMIT=6132607495ae0741e412a63f2acc34e4ccddfc55 +# cashubtc/nuts main as of 2026-08-23T17:08:00Z. A corpus is stale from the moment it is baked; +# the seat's MEMORY.md states this pin so the agent never quotes an old NUT as current. +ARG NUTS_COMMIT=49a909ce4d0739824b3859d4b3da21e6c1abdaeb + +# ============================================================================================= +# builder — mint, seed generator, harness, and the runtime toolchain payload +# ============================================================================================= +FROM ${RUST_IMAGE} AS mint-builder +ARG CDK_VERSION + +# protoc is a BUILD-TIME requirement even with --no-default-features: cdk-mintd depends on +# cdk-signatory unconditionally (the local in-process signatory), and cdk-signatory 0.17.2's +# build.rs compiles src/proto/signatory.proto whether or not the `grpc` feature is on — it panics +# with "Could not find `protoc`" otherwise. Measured, not assumed: the first build of this image +# failed exactly there. protoc lives in this builder stage only; it is not in the final image. +RUN apt-get update \ + && apt-get install -y --no-install-recommends protobuf-compiler \ + && rm -rf /var/lib/apt/lists/* + +# --no-default-features drops cln, lnd, lnbits, bdk, ldk-node, grpc-processor and the management +# RPC. What is left is exactly the isolated testing mode: `fakewallet` (= dep:cdk-fake-wallet) and +# the sqlite store. cdk-mintd's own config comment is explicit that fakewallet "is isolated testing +# mode and cannot be mixed with real payment backends" — building without the real backends means +# this binary CANNOT be pointed at one, which is a stronger guarantee than a config convention. +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,id=cashu-mint-target,target=/tmp/target \ + set -eux; \ + CARGO_TARGET_DIR=/tmp/target cargo install cdk-mintd \ + --version "${CDK_VERSION}" --locked \ + --no-default-features --features fakewallet,sqlite \ + --root /out; \ + strip /out/bin/cdk-mintd + +# The seed generator and the harness are real crates with COMMITTED lockfiles, installed --locked +# (advisor F6 — they were inline heredocs with ranged dependencies and no recorded resolution). +COPY docker/maxplayer-cashu-sandbox/seedgen /src/seedgen +COPY docker/maxplayer-cashu-sandbox/acceptance /src/acceptance +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,id=cashu-mint-target,target=/tmp/target \ + set -eux; \ + CARGO_TARGET_DIR=/tmp/target cargo install --path /src/seedgen --locked --root /out; \ + CARGO_TARGET_DIR=/tmp/target cargo install --path /src/acceptance --locked --root /out; \ + strip /out/bin/test-mint-seed /out/bin/mint-acceptance /out/bin/test-mint-invoice + +# The RUNTIME toolchain payload (advisor F1). Assembled in the same RUN as the registry cache mount, +# because a cache mount is not a layer: it has to be copied into a real directory here or it is gone. +# +# What goes in: the single installed toolchain (rustc, cargo, clippy, rustfmt, rust-std, rust-src) +# and a CARGO_HOME carrying the registry index plus the .crate archives for the whole CDK +# dependency graph. What stays out: rustup itself (a job pins one toolchain and must not fetch +# another), docs and man pages. +# +# `registry/src` is dropped deliberately: cargo extracts sources from `registry/cache/*.crate` on +# first use, so offline builds work without carrying both copies. +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + set -eux; \ + toolchain="$(ls -d /usr/local/rustup/toolchains/* | head -n 1)"; \ + mkdir -p /out/rust; \ + cp -a "${toolchain}" /out/rust/toolchain; \ + rm -rf /out/rust/toolchain/share/doc /out/rust/toolchain/share/man; \ + mkdir -p /out/rust/cargo; \ + cp -a /usr/local/cargo/registry /out/rust/cargo/registry; \ + rm -rf /out/rust/cargo/registry/src; \ + /out/rust/toolchain/bin/rustc --version > /out/rust/TOOLCHAIN.txt; \ + /out/rust/toolchain/bin/cargo --version >> /out/rust/TOOLCHAIN.txt; \ + du -sh /out/rust/toolchain /out/rust/cargo/registry >> /out/rust/TOOLCHAIN.txt + +# ============================================================================================= +# corpus — the spec and CDK source, at pinned commits +# ============================================================================================= +FROM ${DEBIAN_IMAGE} AS corpus +ARG CDK_COMMIT +ARG NUTS_COMMIT +RUN apt-get update \ + && apt-get install -y --no-install-recommends git ca-certificates \ + && rm -rf /var/lib/apt/lists/* +# --filter=blob:none keeps the clone small; the checkout still materialises every file at the pin. +# .git is removed afterwards so the corpus is not an ordinary git working copy inside a job. Note +# this prevents `git pull`, NOT file writes by a root process (advisor N4) — source pinning, not +# runtime immutability. +RUN set -eux; \ + mkdir -p /corpus; \ + git clone --filter=blob:none --no-checkout https://github.com/cashubtc/nuts /corpus/nuts; \ + git -C /corpus/nuts checkout -q "${NUTS_COMMIT}"; \ + rm -rf /corpus/nuts/.git; \ + git clone --filter=blob:none --no-checkout https://github.com/cashubtc/cdk /corpus/cdk; \ + git -C /corpus/cdk checkout -q "${CDK_COMMIT}"; \ + rm -rf /corpus/cdk/.git; \ + printf '%s\n' \ + "cashubtc/nuts ${NUTS_COMMIT}" \ + "cashubtc/cdk ${CDK_COMMIT} (tag v0.17.2)" \ + > /corpus/PINS.txt + +# ============================================================================================= +# the specialist image +# ============================================================================================= +FROM ${BASE_IMAGE} +ARG CDK_VERSION +ARG CDK_COMMIT +ARG NUTS_COMMIT + +COPY --from=mint-builder /out/bin/cdk-mintd /usr/local/bin/cdk-mintd +COPY --from=mint-builder /out/bin/test-mint-seed /usr/local/bin/test-mint-seed +COPY --from=mint-builder /out/bin/test-mint-invoice /usr/local/bin/test-mint-invoice +COPY --from=mint-builder /out/bin/mint-acceptance /usr/local/bin/mint-acceptance +COPY --from=corpus /corpus /opt/cashu/corpus + +# Runtime Rust (advisor F1). +# +# A Rust toolchain plus a warm registry is still not a build environment: rustc shells out to a C +# linker, and this base image ships no compiler at all (measured: no cc, no gcc, no ld, 119 dpkg +# entries). Without these three packages the offline build dies at `linker \`cc\` not found` after +# resolving every dependency correctly — which looks like a dependency problem and is not one. +# +# These are NOT version-pinned, deliberately. Pinning exact Debian package versions makes the image +# unbuildable the moment bookworm rotates a point release, and the reproducibility that matters here +# is the Rust/CDK dependency set, which IS locked. The exact versions installed are recorded into +# /opt/rust/TOOLCHAIN.txt at build time so a replay can be audited rather than assumed. +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends gcc libc6-dev binutils; \ + rm -rf /var/lib/apt/lists/* +COPY --from=mint-builder /out/rust /opt/rust +ENV CARGO_HOME=/opt/rust/cargo \ + PATH=/opt/rust/toolchain/bin:/opt/rust/cargo/bin:$PATH +# A LOGIN shell throws the ENV above away: Debian's /etc/profile assigns PATH unconditionally +# (/etc/profile:5), so `bash -lc rustc` fails in an image where `bash -c rustc` works. That is a +# trap for anything invoking this image through a login shell, which is the common case for a job +# harness, so put the toolchain back on PATH for login shells too rather than relying on callers +# to know. Measured on this image before the fix: `bash -lc` saw the stock six-entry PATH. +RUN printf '%s\n' 'PATH="/opt/rust/toolchain/bin:/opt/rust/cargo/bin:$PATH"' 'export PATH' \ + > /etc/profile.d/10-rust-toolchain.sh \ + && chmod 0644 /etc/profile.d/10-rust-toolchain.sh \ + && bash -lc 'command -v rustc && command -v cargo' >/dev/null +# Offline by default: a specialist job must not silently reach crates.io and call that a warm cache, +# and a contained job may have no route there at all. `--offline` on the command line is redundant +# once this is set; `CARGO_NET_OFFLINE=false` in a job's environment is the deliberate opt-out. +ENV CARGO_NET_OFFLINE=true + +# The curated knowledge topics, INSIDE the image (advisor F2). The injected MEMORY.md index names +# these container paths, so every link in it resolves for a contained job. Non-secret prose only — +# no seat home, no wallet, no key is mounted or copied here. +COPY docker/maxplayer-cashu-sandbox/seat/memory/ /opt/cashu/knowledge/ +# The example is shipped as SOURCE, not as a binary, on purpose: compiling it is the toolchain proof. +COPY docker/maxplayer-cashu-sandbox/examples/ /opt/cashu/examples/ +COPY docker/maxplayer-cashu-sandbox/test-mint.config.toml /opt/cashu/test-mint.config.toml +COPY docker/maxplayer-cashu-sandbox/bin/test-mint /usr/local/bin/test-mint +COPY docker/maxplayer-cashu-sandbox/bin/cashu-toolchain-check /usr/local/bin/cashu-toolchain-check + +# The job runs as a host uid with no passwd entry, so everything it must read has to be +# world-readable and everything it must run world-executable (same rule as the base image's +# `install -m 0755`). +# +# CARGO_HOME is world-writable because cargo takes a lock file inside it (`.package-cache`) on every +# invocation, and the job's uid is not known at build time. This container is a single-principal +# disposable sandbox, so a shared-writable cache costs nothing here; it would be wrong in a +# multi-tenant image. +# +# TEST_MINT_STATE_ROOT is created world-writable for the same reason: the mint's private state lives +# there, OUTSIDE the delivered workdir (advisor F3). +RUN set -eux; \ + chmod 0755 /usr/local/bin/cdk-mintd /usr/local/bin/test-mint-seed \ + /usr/local/bin/test-mint-invoice /usr/local/bin/mint-acceptance \ + /usr/local/bin/test-mint /usr/local/bin/cashu-toolchain-check; \ + chmod -R a+rX /opt/cashu /opt/rust; \ + chmod -R a+rwX /opt/rust/cargo; \ + mkdir -p /var/lib/cashu-test-state; \ + chmod 1777 /var/lib/cashu-test-state; \ + cdk-mintd --version; \ + rustc --version; \ + cargo --version; \ + test-mint --help >/dev/null; \ + cashu-toolchain-check --help >/dev/null; \ + test -f /opt/cashu/knowledge/MEMORY.md; \ + test -f /opt/cashu/examples/wallet-roundtrip/Cargo.lock + +# The offline build proof runs AT BUILD TIME, so the image cannot ship claiming an offline Rust/CDK +# environment it does not have. Both failures found by running it — a login shell losing PATH, and a +# missing C linker — would have shipped silently otherwise. Built in /tmp and deleted: the proof is +# that it compiled, and a stale target/ tree in the image would only be weight. +RUN set -eux; \ + { echo "# runtime build environment, recorded at image build"; \ + rustc --version; \ + cargo --version; \ + gcc --version | head -1; \ + ld --version | head -1; \ + dpkg-query -W -f='gcc ${Version}\n' gcc; \ + dpkg-query -W -f='libc6-dev ${Version}\n' libc6-dev; \ + dpkg-query -W -f='binutils ${Version}\n' binutils; \ + } >> /opt/rust/TOOLCHAIN.txt; \ + chmod 0644 /opt/rust/TOOLCHAIN.txt; \ + cp -a /opt/cashu/examples/wallet-roundtrip /tmp/offline-build-proof; \ + cd /tmp/offline-build-proof; \ + CARGO_TARGET_DIR=/tmp/offline-build-proof-target cargo build --offline --locked; \ + test -x /tmp/offline-build-proof-target/debug/wallet-roundtrip; \ + cd /; \ + rm -rf /tmp/offline-build-proof /tmp/offline-build-proof-target + +LABEL ai.maxplayer.specialist="cashu-cdk" \ + ai.maxplayer.cashu.cdk-version="${CDK_VERSION}" \ + ai.maxplayer.cashu.cdk-commit="${CDK_COMMIT}" \ + ai.maxplayer.cashu.nuts-commit="${NUTS_COMMIT}" \ + ai.maxplayer.cashu.mint-backend="fakewallet (worthless test ecash, loopback only)" \ + ai.maxplayer.cashu.knowledge-path="/opt/cashu/knowledge" \ + ai.maxplayer.cashu.corpus-path="/opt/cashu/corpus" \ + ai.maxplayer.cashu.test-state-root="/var/lib/cashu-test-state" diff --git a/docker/maxplayer-cashu-sandbox/acceptance/Cargo.lock b/docker/maxplayer-cashu-sandbox/acceptance/Cargo.lock new file mode 100644 index 000000000..6d29ae099 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/acceptance/Cargo.lock @@ -0,0 +1,3395 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[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 = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "async-compression" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f10dafd0c8d2e51ae9a748805777613ed0bbe17bf586b76c8311f45c020a32f" +dependencies = [ + "compression-codecs", + "compression-core", + "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.5", +] + +[[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 = "base58ck" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" +dependencies = [ + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[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 = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.8", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" +dependencies = [ + "base58ck", + "base64 0.21.7", + "bech32", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative 0.2.3", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.3.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] + +[[package]] +name = "bitcoin-payment-instructions" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f5b43b0e8338cd36ce7940bd6b8a007d54f1cbe25314ec3fbba566f870b085" +dependencies = [ + "bitcoin", + "lightning", + "lightning-invoice", +] + +[[package]] +name = "bitcoin-units" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" +dependencies = [ + "bitcoin-consensus-encoding", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.3", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[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 = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cashu" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e47bf3a30a044bfad3b1e222aa22d432c5895b28a743f68b8e338af4f12a01" +dependencies = [ + "bitcoin", + "cbor-diag", + "ciborium", + "lightning", + "lightning-invoice", + "once_cell", + "serde", + "serde_json", + "serde_with", + "strum", + "strum_macros", + "thiserror", + "tracing", + "unicode-normalization", + "url", + "uuid", + "web-time", + "zeroize", +] + +[[package]] +name = "cbor-diag" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc245b6ecd09b23901a4fbad1ad975701fd5061ceaef6afa93a2d70605a64429" +dependencies = [ + "bs58", + "chrono", + "data-encoding", + "half", + "nom", + "num-bigint", + "num-rational", + "num-traits", + "separator", + "url", + "uuid", +] + +[[package]] +name = "cc" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cdk" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911586800fc82527ebad109f25470715fd6e6a4f2c98d60cdf0cfed09ab5f75d" +dependencies = [ + "anyhow", + "arc-swap", + "async-trait", + "bitcoin", + "bitcoin-payment-instructions", + "cbor-diag", + "cdk-common", + "cdk-signatory", + "ciborium", + "futures", + "getrandom 0.2.17", + "gloo-timers", + "jsonwebtoken", + "lightning", + "lightning-invoice", + "regex", + "ring", + "rustls", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "url", + "uuid", + "web-time", + "zeroize", +] + +[[package]] +name = "cdk-common" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6157c494a479042c78ce995b62dc10e9b1fe15ba8dfd9fd03c43765f7f87a819" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin", + "cashu", + "cbor-diag", + "cdk-http-client", + "ciborium", + "futures", + "getrandom 0.2.17", + "jsonwebtoken", + "lightning", + "lightning-invoice", + "parking_lot", + "paste", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tonic", + "tracing", + "url", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "cdk-fake-wallet" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a564a9ebede8ecd625fdaaab25540c2b79f28f6067aa9c7497cf9f5c6af31" +dependencies = [ + "async-trait", + "bitcoin", + "cdk-common", + "futures", + "lightning", + "lightning-invoice", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "uuid", + "web-time", +] + +[[package]] +name = "cdk-http-client" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c808588b6d33f67c72b9e5d52661295166bff0661b38b281803a8eea6cf97f" +dependencies = [ + "futures", + "futures-channel", + "js-sys", + "regex", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio-tungstenite", + "tracing", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "cdk-signatory" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c024fd98d57f0d4ea22bf562b8e765b9a0e87199670de69a2b8a1d5d8b47f8c7" +dependencies = [ + "anyhow", + "async-trait", + "bip39", + "bitcoin", + "cdk-common", + "clap", + "getrandom 0.2.17", + "home", + "rustls", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cdk-sql-common" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816a5cb0461dbf32b042d14dd0fe44f788a75bdee9e78830ad9e6d35654bbd6d" +dependencies = [ + "async-trait", + "bitcoin", + "cdk-common", + "lightning-invoice", + "once_cell", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "cdk-sqlite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab7a6a12ed31086575404514b03729295690ce5d14ec7a55f9b2ab34d45a425" +dependencies = [ + "async-trait", + "bitcoin", + "cdk-common", + "cdk-sql-common", + "lightning-invoice", + "paste", + "rusqlite", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[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.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 = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "compression-codecs" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a6d0db8759036a783bc7c3f7a07f8cef3bf9470eb1db3bc86e8bcd1c5d0fe8" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414" + +[[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 = "crc32fast" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.5", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[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.5", +] + +[[package]] +name = "dnssec-prover" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9468f1a08c50bd1e5ad91b151e11ce8e806f8fa1c1eb9b07f66c7011de45a2e" +dependencies = [ + "bitcoin_hashes", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[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 = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[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 = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[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-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[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-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[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 = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[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 = "hex-conservative" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[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 = "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", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[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 = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[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.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[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 = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lightning" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab16d2a714c0b26d7230bd388ac383a30fce231c8927c62752afc0471a36dc6" +dependencies = [ + "bech32", + "bitcoin", + "dnssec-prover", + "hashbrown 0.13.2", + "libm", + "lightning-invoice", + "lightning-macros", + "lightning-types", + "possiblyrandom", +] + +[[package]] +name = "lightning-invoice" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d83bd798e04ab9eecc8bbef1fa17d3808859bcdc0406bd16c55d51c8834444" +dependencies = [ + "bech32", + "bitcoin", + "lightning-types", + "serde", +] + +[[package]] +name = "lightning-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c717494cdc2c8bb85bee7113031248f5f6c64f8802b33c1c9e2d98e594aa71" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "lightning-types" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77c676d4a34cceb2ae3756916e446b4d17f9430a24107e099981f0f9aec77e6" +dependencies = [ + "bitcoin", +] + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[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 = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mint-acceptance" +version = "0.1.0" +dependencies = [ + "anyhow", + "cdk", + "cdk-fake-wallet", + "cdk-sqlite", + "rand 0.9.5", + "serde_json", + "tokio", +] + +[[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 = "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-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[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 = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[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 = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "possiblyrandom" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c564dbf654befd49035528299f1208a40508f6e07efb11c163444e304e4484f" +dependencies = [ + "getrandom 0.2.17", +] + +[[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 = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "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 = [ + "libc", + "rand_chacha 0.3.1", + "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 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[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 2.13.1", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[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.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[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 = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.13.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustls" +version = "0.23.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" +dependencies = [ + "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-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "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 = "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 = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.8", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.1", + "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 = "separator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" + +[[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_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.5", +] + +[[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_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 = "serde_with" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" +dependencies = [ + "base64 0.23.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.2", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[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 = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" + +[[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 = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +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 = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[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.5", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[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 = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "base64 0.22.1", + "bytes", + "http", + "http-body", + "http-body-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + +[[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", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "async-compression", + "bitflags 2.13.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tokio", + "tokio-util", + "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 = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[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 = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[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.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" +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-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[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.5", +] + +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf06bd8162af0734b344780deb55b42a2429ae430870d13fcc12f238e880fe6e" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae42c0555055784c70058d19ba8e275528e8a99a706684868ace5da4e716a4ab" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/docker/maxplayer-cashu-sandbox/acceptance/Cargo.toml b/docker/maxplayer-cashu-sandbox/acceptance/Cargo.toml new file mode 100644 index 000000000..e50210777 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/acceptance/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "mint-acceptance" +version = "0.1.0" +edition = "2021" +publish = false + +# Pinned to the SAME CDK the workspace pins (=0.17.2). The point of the harness is to prove the +# mint and the wallet our seller actually links against agree; a different wallet version would +# prove something else. +[dependencies] +cdk = { version = "=0.17.2", default-features = false, features = ["wallet"] } +cdk-sqlite = "=0.17.2" +# for FakeInvoiceDescription / create_fake_invoice — the exact structs cdk-mintd's fakewallet +# backend parses, so the failure-injection payload cannot drift from what the mint reads. +cdk-fake-wallet = "=0.17.2" +tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] } +rand = "0.9" +serde_json = "1" +anyhow = "1" + +[[bin]] +name = "mint-acceptance" +path = "src/main.rs" + +# Standalone crate root. These image-local crates are deliberately NOT members of the +# product workspace: adding them there would drag image tooling into every product build, and +# an empty [workspace] table is the supported way to say "this is its own root". +[workspace] diff --git a/docker/maxplayer-cashu-sandbox/acceptance/src/bin/predicate-regression.rs b/docker/maxplayer-cashu-sandbox/acceptance/src/bin/predicate-regression.rs new file mode 100644 index 000000000..1b73d18d8 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/acceptance/src/bin/predicate-regression.rs @@ -0,0 +1,286 @@ +//! predicate-regression — targeted proof that the corrected F4/F5 predicates DISCRIMINATE. +//! +//! The accepted 39-assertion gate run is not re-run here and is not evidence for this round. What +//! is in question is narrower and sharper: the old oracles could pass while value was stranded, and +//! a green transcript from a healthy fixture cannot tell you whether that hole is closed. Only a +//! case the old predicate accepts and the new predicate rejects can. +//! +//! So this binary builds the reviewer's own counterexample for real, against the real mint, and +//! requires the old predicate to PASS on it and the new predicate to FAIL on it. If a future edit +//! weakens a predicate back, this exits non-zero. +//! +//! Two fixtures, both real wallets against the loopback fakewallet mint: +//! +//! A. HEALTHY — issue, sweep the whole balance, everything arrives. Both old and new predicates +//! must pass. This is the control: a regression gate that only ever fails proves nothing. +//! B. STRANDED RESERVE — the reviewer's case. Fund a wallet, abandon a `prepare_send` so the +//! proofs sit in Reserved, then sweep what is left. The old drain predicate (spendable and +//! pending only) and the old derived-fee conservation sum both PASS while most of the value is +//! stranded. The corrected predicates must both FAIL. +//! +//! Worthless test ecash, loopback only. No real funds and no external payment. + +use std::sync::Arc; +use std::time::Duration; + +use anyhow::{anyhow, bail, Context, Result}; +use cdk::nuts::nut00::ProofsMethods; +use cdk::nuts::{CurrencyUnit, PaymentMethod}; +use cdk::wallet::{ReceiveOptions, SendOptions, Wallet}; +use cdk::Amount; +use cdk_sqlite::wallet::memory; +use rand::random; + +const MINT_URL: &str = "http://127.0.0.1:8085"; + +// --------------------------------------------------------------------------------------------- +// The predicates themselves, as pure functions, so the regression compares ORACLES and not prose. +// The `old_*` forms are the exact shapes that shipped and were faulted; they are kept here only so +// the difference can be measured, and are never used to judge the mint. +// --------------------------------------------------------------------------------------------- + +/// Shipped and faulted: spendable and pending only. Reserved is a third, distinct pool +/// (cdk 0.17.2 `wallet/balance.rs:23-34`), so this cannot see value abandoned in reserve. +fn old_drained(spendable: u64, pending: u64) -> bool { + spendable == 0 && pending == 0 +} + +/// Corrected: all three pools. +fn new_drained(spendable: u64, pending: u64, reserved: u64) -> bool { + spendable == 0 && pending == 0 && reserved == 0 +} + +/// Shipped and faulted: the "fee" is whatever went missing (`before - locked`), so +/// `received + fee == before` holds for ANY shortfall, by construction. +fn old_conserved_derived_fee(before: u64, received: u64) -> bool { + let derived_fee = before.saturating_sub(received); + received + derived_fee == before +} + +/// Corrected: the fee must be the one CDK QUOTED for the send. +fn new_conserved_quoted_fee(before: u64, received: u64, quoted_fee: u64) -> bool { + received + quoted_fee == before +} + +/// Shipped and faulted: any positive reclaim counts, so a partial reclaim passes. +fn old_recovery_complete(reclaimed: u64) -> bool { + reclaimed > 0 +} + +/// Corrected: the whole stranded amount, and the funded total spendable again with nothing left in +/// either non-spendable pool. +fn new_recovery_complete( + reclaimed: u64, + stranded: u64, + spendable: u64, + funded: u64, + pending: u64, + reserved: u64, +) -> bool { + reclaimed == stranded && spendable == funded && pending == 0 && reserved == 0 +} + +struct Report { + rows: Vec<(String, bool)>, +} + +impl Report { + fn new() -> Self { + Self { rows: Vec::new() } + } + + /// `expected` is what this regression REQUIRES the oracle to say about this fixture. + fn require(&mut self, what: &str, actual: bool, expected: bool, detail: &str) -> Result<()> { + let ok = actual == expected; + let verdict = if actual { "PASS" } else { "FAIL" }; + println!( + " {} {what}\n oracle says {verdict}, required {} — {detail}", + if ok { "ok " } else { "BAD " }, + if expected { "PASS" } else { "FAIL" }, + ); + self.rows.push((what.to_owned(), ok)); + if ok { + Ok(()) + } else { + Err(anyhow!("{what}: oracle said {verdict}, required {}", if expected { "PASS" } else { "FAIL" })) + } + } +} + +async fn wallet() -> Result { + let store = Arc::new(memory::empty().await?); + Ok(Wallet::new(MINT_URL, CurrencyUnit::Sat, store, random::<[u8; 64]>(), None)?) +} + +async fn issue(w: &Wallet, amount: u64) -> Result { + let quote = w + .mint_quote(PaymentMethod::BOLT11, Some(Amount::from(amount)), None, None) + .await + .context("mint_quote")?; + let proofs = w + .wait_and_mint_quote(quote, Default::default(), Default::default(), Duration::from_secs(30)) + .await + .context("wait_and_mint_quote")?; + Ok(u64::from(proofs.total_amount()?)) +} + +async fn pools(w: &Wallet) -> Result<(u64, u64, u64)> { + Ok(( + u64::from(w.total_balance().await?), + u64::from(w.total_pending_balance().await?), + u64::from(w.total_reserved_balance().await?), + )) +} + +#[tokio::main] +async fn main() -> Result<()> { + println!("predicate-regression — do the corrected F4/F5 oracles actually discriminate?"); + println!("mint {MINT_URL} (fakewallet, worthless test ecash)\n"); + let mut r = Report::new(); + + // The zero-fee contract every exact-value assertion here depends on, read from the MINT. + let probe = wallet().await?; + let keyset = probe.fetch_active_keyset().await?; + if keyset.input_fee_ppk != 0 { + bail!( + "fixture precondition broken: active keyset charges input_fee_ppk {}; the exact-value \ + predicates below would be invalid, so refusing to report on them", + keyset.input_fee_ppk + ); + } + println!("[precondition] active keyset {} charges input_fee_ppk 0\n", keyset.id); + + // ----------------------------------------------------------------- fixture A: healthy + println!("[A] HEALTHY sweep — the control. Both oracles must pass."); + let a = wallet().await?; + let funded_a = issue(&a, 40).await?; + let prep_a = a.prepare_send(Amount::from(funded_a), SendOptions::default()).await?; + let quoted_a = u64::from(prep_a.fee()); + let locked_a = u64::from(prep_a.amount()); + let token_a = prep_a.confirm(None).await?.to_string(); + let sink_a = wallet().await?; + let got_a = u64::from(sink_a.receive(&token_a, ReceiveOptions::default()).await?); + let (sp_a, pe_a, rs_a) = pools(&a).await?; + println!( + " funded {funded_a}, locked {locked_a}, quoted fee {quoted_a}, received {got_a}; \ + source pools spendable {sp_a} pending {pe_a} reserved {rs_a}" + ); + r.require( + "A: old drain oracle", + old_drained(sp_a, pe_a), + true, + "nothing is actually stranded, so the weak oracle is right here too", + )?; + r.require( + "A: corrected drain oracle", + new_drained(sp_a, pe_a, rs_a), + true, + "all three pools empty", + )?; + r.require( + "A: corrected quoted-fee conservation", + new_conserved_quoted_fee(funded_a, got_a, quoted_a), + true, + "every sat arrived and the quoted fee was zero", + )?; + + // ----------------------------------------------------------------- fixture B: stranded reserve + // The reviewer's counterexample, built for real: abandon a prepare_send so its proofs stay in + // Reserved, then sweep only what is still spendable. + println!("\n[B] STRANDED RESERVE — the counterexample. Old oracles must pass, corrected must fail."); + let b = wallet().await?; + let funded_b = issue(&b, 43).await?; + let abandoned = b.prepare_send(Amount::from(42), SendOptions::default()).await?; + let abandoned_amount = u64::from(abandoned.amount()); + drop(abandoned); // never confirmed, never cancelled: the proofs stay reserved + let (sp_b0, pe_b0, rs_b0) = pools(&b).await?; + if rs_b0 == 0 { + bail!( + "fixture B did not strand anything (reserved 0 after abandoning a {abandoned_amount} \ + sat prepare_send); the counterexample would be vacuous, so refusing to report a pass" + ); + } + println!(" funded {funded_b}, abandoned prepare_send of {abandoned_amount}; pools now spendable {sp_b0} pending {pe_b0} reserved {rs_b0}"); + + // Sweep what is left, exactly as the gate's sweep leg would. + let (got_b, quoted_b) = if sp_b0 > 0 { + let prep_b = b.prepare_send(Amount::from(sp_b0), SendOptions::default()).await?; + let quoted = u64::from(prep_b.fee()); + let token_b = prep_b.confirm(None).await?.to_string(); + let sink_b = wallet().await?; + (u64::from(sink_b.receive(&token_b, ReceiveOptions::default()).await?), quoted) + } else { + (0, 0) + }; + let (sp_b, pe_b, rs_b) = pools(&b).await?; + println!( + " swept {got_b} with quoted fee {quoted_b}; pools now spendable {sp_b} pending {pe_b} \ + reserved {rs_b} — {rs_b} sats stranded out of {funded_b}" + ); + + r.require( + "B: old drain oracle accepts a wallet with value stranded in Reserved", + old_drained(sp_b, pe_b), + true, + "this is the defect, reproduced: it reports fully drained", + )?; + r.require( + "B: corrected drain oracle REJECTS it", + new_drained(sp_b, pe_b, rs_b), + false, + "reserved is non-zero, so the wallet is not drained", + )?; + r.require( + "B: old derived-fee conservation accepts the shortfall as a fee", + old_conserved_derived_fee(funded_b, got_b), + true, + "this is the defect, reproduced: the missing value is relabelled a fee", + )?; + r.require( + "B: corrected quoted-fee conservation REJECTS it", + new_conserved_quoted_fee(funded_b, got_b, quoted_b), + false, + "CDK quoted no fee, so the shortfall has no explanation", + )?; + + // ----------------------------------------------------------------- F4 recovery oracle + // Arithmetic discrimination on the recovery oracles. Stated plainly: this does NOT force the + // mint to perform a partial reclaim — the observed reclaim in the accepted run was complete. + // What is checked is that the corrected oracle would reject a partial one, using the same real + // magnitudes (12 stranded of 32 funded) the gate uses. + println!("\n[C] RECOVERY oracles on a partial reclaim (arithmetic on the oracles, not a forced mint failure)."); + let (funded_c, stranded_c, partial_c) = (32u64, 12u64, 1u64); + r.require( + "C: old recovery oracle accepts reclaiming 1 of 12", + old_recovery_complete(partial_c), + true, + "this is the defect: any positive reclaim passed", + )?; + r.require( + "C: corrected recovery oracle REJECTS reclaiming 1 of 12", + new_recovery_complete(partial_c, stranded_c, funded_c - stranded_c + partial_c, funded_c, 0, 0), + false, + "partial reclaim, so the funded total is not restored", + )?; + r.require( + "C: corrected recovery oracle accepts a COMPLETE reclaim", + new_recovery_complete(stranded_c, stranded_c, funded_c, funded_c, 0, 0), + true, + "whole stranded amount back, funded total spendable, both other pools empty", + )?; + + let bad = r.rows.iter().filter(|(_, ok)| !ok).count(); + println!("\n====================================================="); + if bad == 0 { + println!("PASS — {} oracle requirements met across 3 fixtures", r.rows.len()); + println!("The corrected predicates reject a case the shipped ones accepted."); + } else { + println!("FAIL — {bad} of {} oracle requirements unmet", r.rows.len()); + } + println!("====================================================="); + if bad == 0 { + Ok(()) + } else { + bail!("{bad} oracle requirement(s) unmet") + } +} diff --git a/docker/maxplayer-cashu-sandbox/acceptance/src/bin/test-mint-invoice.rs b/docker/maxplayer-cashu-sandbox/acceptance/src/bin/test-mint-invoice.rs new file mode 100644 index 000000000..8069e4e6c --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/acceptance/src/bin/test-mint-invoice.rs @@ -0,0 +1,37 @@ +//! test-mint-invoice — print a BOLT11 string the sandbox fakewallet mint will settle (or refuse). +//! +//! Usage: test-mint-invoice [fail] +//! +//! `fail` injects a genuine payment failure. Both `pay_err` AND the two states must say so: the +//! fake backend records `check_payment_state` into its payment-states map BEFORE it honours +//! `pay_err` (cdk-fake-wallet 0.17.2 src/lib.rs:706-714), so leaving the states at their `Paid` +//! default produces a melt that finalises `Paid` for an invoice the backend refused. + +use cdk::nuts::MeltQuoteState; +use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription}; + +fn main() { + let mut args = std::env::args().skip(1); + let msat: u64 = match args.next().map(|a| a.parse()) { + Some(Ok(v)) => v, + _ => { + eprintln!("usage: test-mint-invoice [fail]"); + std::process::exit(64); + } + }; + let fail = args.next().is_some_and(|a| a == "fail"); + + let description = if fail { + FakeInvoiceDescription { + pay_invoice_state: MeltQuoteState::Unpaid, + check_payment_state: MeltQuoteState::Unpaid, + pay_err: true, + check_err: false, + } + } else { + FakeInvoiceDescription::default() + }; + + let json = serde_json::to_string(&description).expect("serialize fake invoice description"); + println!("{}", create_fake_invoice(msat, json)); +} diff --git a/docker/maxplayer-cashu-sandbox/acceptance/src/main.rs b/docker/maxplayer-cashu-sandbox/acceptance/src/main.rs new file mode 100644 index 000000000..0b1190b29 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/acceptance/src/main.rs @@ -0,0 +1,649 @@ +//! mint-acceptance — the named acceptance gate for the sandbox-local fakewallet test mint. +//! +//! Every check asserts a NUMBER or an EXACT protocol outcome, never merely "the call returned". +//! A no-op pass is a failure here: if a leg cannot be run, the harness bails rather than printing +//! a green tick. +//! +//! Four things earlier versions got wrong, all found by independent review and all fixed here, +//! because they are the difference between coverage and the appearance of coverage: +//! +//! * negative legs accepted ANY error. A transport failure is not proof that a double spend was +//! rejected, nor that a payment did not happen. Every negative leg now matches the EXACT cdk +//! error variant it claims (`Error::TokenAlreadySpent`, `Error::PaymentFailed`). +//! * post-restart "no loss" was a local tautology: `total_balance` reads the wallet's own +//! localstore (`wallet/balance.rs:10-20`), not mint state, so comparing it either side of a +//! mint restart proves nothing about the mint. The restart leg now SPENDS THE WHOLE residual +//! balance against the restarted mint and reconciles the received total exactly. +//! +//! * conservation was checked against spendable and pending only. Reserved is a THIRD, distinct +//! pool (`wallet/balance.rs:23-34`), so value abandoned in reserve was invisible to the very +//! check that claimed nothing was stranded. Every drain assertion now covers all three. +//! * the sweep derived its fee as `before - locked` — that is not a fee, it is whatever went +//! missing, so any unexplained shortfall satisfied `swept + fees == total` by construction. The +//! fee is now the one CDK QUOTED, the fixture's zero-fee keyset is asserted against the mint +//! rather than trusted from config, and unrelated preparation errors are propagated instead of +//! being swallowed by an amount search. +//! +//! Recovery is likewise exercised from a genuinely non-empty unresolved state, across a wallet +//! restart backed by a real sqlite file, not with in-memory stores that never reopen — and both +//! outcomes recovery may produce are held to the same complete-value contract, since a partial +//! reclaim satisfies `reclaimed > 0` and is still a loss. +//! +//! Worthless test ecash. This binary talks to http://127.0.0.1:8085 and nothing else. + +use std::collections::HashMap; +use std::net::{IpAddr, SocketAddr, TcpStream, UdpSocket}; +use std::process::Command; +use std::sync::Arc; +use std::time::Duration; + +use anyhow::{anyhow, bail, Context, Result}; +use cdk::nuts::nut00::ProofsMethods; +use cdk::nuts::{CurrencyUnit, MeltQuoteState, PaymentMethod}; +use cdk::wallet::{ReceiveOptions, SendOptions, Wallet}; +use cdk::{Amount, Error as CdkError}; +use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription}; +use cdk_sqlite::wallet::{memory, WalletSqliteDatabase}; +use rand::random; + +const MINT_URL: &str = "http://127.0.0.1:8085"; +const MINT_PORT: u16 = 8085; + +struct Tally { + checks: u32, + legs: Vec, +} + +impl Tally { + fn new() -> Self { + Self { checks: 0, legs: Vec::new() } + } + + fn check(&mut self, what: &str, ok: bool, detail: impl AsRef) -> Result<()> { + self.checks += 1; + let detail = detail.as_ref(); + if ok { + println!(" ok {what}: {detail}"); + Ok(()) + } else { + println!(" FAIL {what}: {detail}"); + Err(anyhow!("{what}: {detail}")) + } + } + + fn leg(&mut self, name: &str) { + println!("\n[{}] {name}", self.legs.len() + 1); + self.legs.push(name.to_owned()); + } +} + +async fn memory_wallet() -> Result { + let store = Arc::new(memory::empty().await?); + Ok(Wallet::new(MINT_URL, CurrencyUnit::Sat, store, random::<[u8; 64]>(), None)?) +} + +/// A wallet on a real sqlite FILE, so it can be dropped and reopened — a wallet restart. +async fn file_wallet(path: &str, seed: [u8; 64]) -> Result { + let store = Arc::new(WalletSqliteDatabase::new(path).await?); + Ok(Wallet::new(MINT_URL, CurrencyUnit::Sat, store, seed, None)?) +} + +async fn balance(w: &Wallet) -> Result { + Ok(u64::from(w.total_balance().await?)) +} + +async fn issue(w: &Wallet, amount: u64) -> Result { + let quote = w + .mint_quote(PaymentMethod::BOLT11, Some(Amount::from(amount)), None, None) + .await + .context("mint_quote")?; + let proofs = w + .wait_and_mint_quote(quote, Default::default(), Default::default(), Duration::from_secs(30)) + .await + .context("wait_and_mint_quote")?; + Ok(u64::from(proofs.total_amount()?)) +} + +/// One token carrying a wallet's whole spendable balance, with the fee CDK actually QUOTED for it. +struct SweptToken { + token: String, + /// Spendable balance immediately before the send. + before: u64, + /// Amount CDK locked into the token. + locked: u64, + /// Fee CDK QUOTED for the send — not a residual derived from what went missing. + quoted_fee: u64, +} + +/// Send the wallet's ENTIRE spendable balance in one operation. This is what makes the restart leg +/// meaningful: the mint has to honour every remaining proof, not just one sat of them. +/// +/// The first version walked down from the full balance trying every smaller amount, swallowing each +/// error, and the caller then inferred `fee = before - locked`. Two defects, both found by review. +/// An inferred fee is simply whatever went missing, so ANY unexplained shortfall satisfied the +/// conservation sum by construction; and discarding every preparation error turned unrelated +/// failures into fee-search candidates. Concretely: a wallet holding 43 that sent 42 with 1 sat +/// stranded in Reserved would have had the missing sat relabelled a fee, and passed. +/// +/// So — one preparation, for the whole balance, fee RETURNED rather than derived, every error +/// propagated. The active keyset on this fixture charges `input_fee_ppk = 0`, asserted against the +/// mint itself in the recovery leg, and that is what makes a full-balance send preparable at all. +/// A failure here is therefore a real finding and must not be searched around. +async fn send_full_balance(w: &Wallet) -> Result> { + let before = balance(w).await?; + if before == 0 { + return Ok(None); + } + let prepared = w + .prepare_send(Amount::from(before), SendOptions::default()) + .await + .with_context(|| format!("prepare_send of the full {before} sat balance"))?; + let locked = u64::from(prepared.amount()); + let quoted_fee = u64::from(prepared.fee()); + let token = prepared.confirm(None).await?.to_string(); + Ok(Some(SweptToken { token, before, locked, quoted_fee })) +} + +/// True when this is the exact cdk error the leg claims. `matches!` on the variant, not a substring +/// of the message: an error message can change wording without changing meaning, and a substring +/// match would also accept an unrelated error that happens to contain the phrase. +fn is_already_spent(e: &CdkError) -> bool { + matches!(e, CdkError::TokenAlreadySpent) +} + +fn is_payment_failed(e: &CdkError) -> bool { + matches!(e, CdkError::PaymentFailed) +} + +/// The mint's own listening socket, read from the kernel rather than inferred from config. +/// /proc/net/tcp local_address for a loopback bind is `0100007F` (127.0.0.1, little-endian hex); +/// a wildcard bind would be `00000000`. +fn listener_local_addrs() -> Result> { + let mut found = Vec::new(); + let want_port = format!("{MINT_PORT:04X}"); + for path in ["/proc/net/tcp", "/proc/net/tcp6"] { + let Ok(text) = std::fs::read_to_string(path) else { continue }; + for line in text.lines().skip(1) { + let mut cols = line.split_whitespace(); + let _sl = cols.next(); + let Some(local) = cols.next() else { continue }; + let Some(_rem) = cols.next() else { continue }; + let Some(state) = cols.next() else { continue }; + if state != "0A" { + continue; // 0A = TCP_LISTEN + } + if let Some((addr, port)) = local.split_once(':') { + if port.eq_ignore_ascii_case(&want_port) { + found.push(addr.to_owned()); + } + } + } + } + Ok(found) +} + +/// This container's own routable address, without sending a packet. +fn container_ip() -> Option { + let sock = UdpSocket::bind("0.0.0.0:0").ok()?; + sock.connect("192.0.2.1:9").ok()?; + sock.local_addr().ok().map(|a| a.ip()).filter(|ip| !ip.is_loopback()) +} + +fn test_mint(args: &[&str]) -> Result { + let out = Command::new("test-mint").args(args).output().context("run test-mint")?; + if !out.status.success() { + bail!("test-mint {args:?} failed: {}", String::from_utf8_lossy(&out.stderr)); + } + Ok(String::from_utf8_lossy(&out.stdout).trim().to_owned()) +} + +#[tokio::main] +async fn main() -> Result<()> { + println!("mint-acceptance — sandbox-local fakewallet mint at {MINT_URL}"); + println!("WORTHLESS TEST ECASH. No real funds, no external payment.\n"); + + let mut t = Tally::new(); + + // Private scratch for the file-backed wallet. Deliberately under the mint's state root, which + // the controller guarantees is OUTSIDE the delivered workdir. + let state_root = + std::env::var("TEST_MINT_STATE_ROOT").unwrap_or_else(|_| "/var/lib/cashu-test-state".into()); + let wallet_dir = format!("{state_root}/acceptance-wallet"); + std::fs::create_dir_all(&wallet_dir)?; + let wallet_db = format!("{wallet_dir}/recovery.sqlite"); + let _ = std::fs::remove_file(&wallet_db); + + // ------------------------------------------------------------------ 1. mint info + t.leg("mint info (NUT-06)"); + let wallet = memory_wallet().await?; + let info = wallet.fetch_mint_info().await?.ok_or_else(|| anyhow!("mint returned no info"))?; + let version = info.version.as_ref().map(|v| v.to_string()).unwrap_or_default(); + t.check("mint version is exactly cdk-mintd/0.17.2", version == "cdk-mintd/0.17.2", &version)?; + t.check( + "mint advertises NUT-04 mint support", + !info.nuts.nut04.methods.is_empty(), + format!("{} method(s)", info.nuts.nut04.methods.len()), + )?; + + // ------------------------------------------------------------------ 2. issue + t.leg("mint quote + issue"); + const ISSUE: u64 = 64; + let minted = issue(&wallet, ISSUE).await?; + t.check("proofs minted", minted == ISSUE, format!("{minted} sats (wanted {ISSUE})"))?; + let after_issue = balance(&wallet).await?; + t.check("wallet balance after issue", after_issue == ISSUE, format!("{after_issue} sats"))?; + + // ------------------------------------------------------------------ 3. send / receive + t.leg("send + receive across wallets"); + const SEND: u64 = 21; + let prepared = wallet.prepare_send(Amount::from(SEND), SendOptions::default()).await?; + let send_fee = u64::from(prepared.fee()); + let token_str = prepared.confirm(None).await?.to_string(); + t.check( + "token is a cashu token", + token_str.starts_with("cashu"), + format!("{} chars", token_str.len()), + )?; + let sender_after = balance(&wallet).await?; + let expect_sender = ISSUE - SEND - send_fee; + t.check( + "sender debited exactly amount+fee", + sender_after == expect_sender, + format!("{sender_after} sats (issued {ISSUE} - sent {SEND} - fee {send_fee})"), + )?; + + let receiver = memory_wallet().await?; + let received = u64::from(receiver.receive(&token_str, ReceiveOptions::default()).await?); + t.check("receiver credited", received == SEND, format!("{received} sats (wanted {SEND})"))?; + + // ------------------------------------------------------------------ 4. double spend + t.leg("double spend rejected with the exact protocol error"); + let thief = memory_wallet().await?; + match thief.receive(&token_str, ReceiveOptions::default()).await { + Ok(a) => t.check( + "second receive of the same token", + false, + format!("ACCEPTED {} sats — double spend!", u64::from(a)), + )?, + Err(e) => t.check( + "second receive fails with Error::TokenAlreadySpent", + is_already_spent(&e), + format!("{e:?}"), + )?, + } + let thief_bal = balance(&thief).await?; + t.check("double-spender balance stays zero", thief_bal == 0, format!("{thief_bal} sats"))?; + + // ------------------------------------------------------------------ 5. melt + t.leg("melt"); + const MELT: u64 = 5; + let good_invoice = + create_fake_invoice(MELT * 1_000, serde_json::to_string(&FakeInvoiceDescription::default())?); + let before_melt = balance(&receiver).await?; + let melt_quote = receiver + .melt_quote(PaymentMethod::BOLT11, good_invoice.to_string(), None, None) + .await?; + let prepared_melt = receiver.prepare_melt(&melt_quote.id, HashMap::new()).await?; + let melt_amount = u64::from(prepared_melt.amount()); + let confirmed = prepared_melt.confirm().await?; + let fee_paid = u64::from(confirmed.fee_paid()); + let after_melt = balance(&receiver).await?; + t.check("melt amount", melt_amount == MELT, format!("{melt_amount} sats"))?; + t.check( + "melt finalised Paid", + format!("{:?}", confirmed.state()).eq_ignore_ascii_case("Paid"), + format!("{:?}", confirmed.state()), + )?; + t.check( + "balance fell by exactly amount+fee", + after_melt == before_melt - MELT - fee_paid, + format!("{before_melt} -> {after_melt} (melt {MELT}, fee {fee_paid})"), + )?; + + // ------------------------------------------------------------------ 6. failed payment + // cdk-fake-wallet reads the invoice DESCRIPTION as a FakeInvoiceDescription. `pay_err` alone is + // NOT enough: the backend records `check_payment_state` into its payment-states map BEFORE it + // honours pay_err (cdk-fake-wallet 0.17.2 src/lib.rs:706-714), so with the struct's Paid + // default the mint's follow-up status check reports PAID and the melt finalises Paid for an + // invoice the backend refused. Both states must say Unpaid. + t.leg("failed payment: exact error, terminal quote state, nothing lost"); + let fail_desc = FakeInvoiceDescription { + pay_invoice_state: MeltQuoteState::Unpaid, + check_payment_state: MeltQuoteState::Unpaid, + pay_err: true, + check_err: false, + }; + let bad_invoice = create_fake_invoice(MELT * 1_000, serde_json::to_string(&fail_desc)?); + let before_fail = balance(&receiver).await?; + let fail_quote = receiver + .melt_quote(PaymentMethod::BOLT11, bad_invoice.to_string(), None, None) + .await?; + let fail_quote_id = fail_quote.id.clone(); + let prepared_fail = receiver.prepare_melt(&fail_quote_id, HashMap::new()).await?; + match prepared_fail.confirm().await { + Ok(f) => t.check( + "melt against a refusing backend", + false, + format!("SUCCEEDED state={:?} amount={}", f.state(), u64::from(f.amount())), + )?, + Err(e) => t.check( + "melt fails with Error::PaymentFailed", + is_payment_failed(&e), + format!("{e:?}"), + )?, + } + // Reconcile the QUOTE's terminal state with the mint, not just the local error. + let quote_state = receiver.check_melt_quote_status(&fail_quote_id).await?; + let quote_state_name = format!("{:?}", quote_state.state); + t.check( + "mint reports the failed quote as Unpaid", + quote_state.state == MeltQuoteState::Unpaid, + "e_state_name, + )?; + let after_fail = balance(&receiver).await?; + let pending_after_fail = u64::from(receiver.total_pending_balance().await?); + let reserved_after_fail = u64::from(receiver.total_reserved_balance().await?); + t.check( + "no test ecash lost to the failed payment", + after_fail == before_fail, + format!("{before_fail} -> {after_fail} sats"), + )?; + t.check( + "nothing left stranded pending or reserved", + pending_after_fail == 0 && reserved_after_fail == 0, + format!("pending {pending_after_fail}, reserved {reserved_after_fail}"), + )?; + + // ------------------------------------------------------------------ 7. recovery + // A genuinely non-empty unresolved state, across a WALLET-OBJECT AND STORE REOPEN. `confirm()` + // on a send leaves the saga in TokenCreated with the proofs committed to a token nobody has + // redeemed; dropping the wallet and reopening the same sqlite FILE is the reopen. Then recovery + // must find it AND give the whole funded value back. + // + // Scope, stated exactly so this is not read as more than it is: the wallet object is dropped and + // the same sqlite file is reopened IN THE SAME PROCESS, with the seed held in memory. This is + // not an OS-level process crash, not a seed-only restore with no local database, not an + // interrupted melt, and not a crash injected at every intermediate saga stage. + // + // The oracle is complete expected VALUE, not "an error did not happen" and not `reclaimed > 0`: + // a partial reclaim satisfies a positivity check and is still a loss. Both outcomes recovery is + // allowed to produce — driven forward, or compensated — must land on the same full total with + // pending AND reserved both empty. + t.leg("recovery of an unresolved send across a wallet-object/store reopen"); + let seed: [u8; 64] = random(); + const RECOVER_FUND: u64 = 32; + const RECOVER_SEND: u64 = 12; + let stranded_amount; + { + let w = file_wallet(&wallet_db, seed).await?; + // The zero-fee contract this leg and the sweep leg both rely on, read from the MINT's active + // keyset rather than trusted from test-mint.config.toml. If the fixture ever sets a non-zero + // input fee, every exact-value assertion below becomes wrong, so refuse to run rather than + // reinterpret the difference as an acceptable fee. + let keyset = w.fetch_active_keyset().await?; + t.check( + "fixture precondition: active keyset charges no input fee", + keyset.input_fee_ppk == 0, + format!("input_fee_ppk {} on keyset {}", keyset.input_fee_ppk, keyset.id), + )?; + let funded = issue(&w, RECOVER_FUND).await?; + t.check("recovery wallet funded", funded == RECOVER_FUND, format!("{funded} sats"))?; + let prep = w.prepare_send(Amount::from(RECOVER_SEND), SendOptions::default()).await?; + let prep_fee = u64::from(prep.fee()); + t.check( + "CDK quotes a zero fee for this send, as the keyset implies", + prep_fee == 0, + format!("quoted fee {prep_fee} sats"), + )?; + stranded_amount = u64::from(prep.amount()); + // Token created and deliberately NEVER redeemed: this is the unresolved state. + let _token = prep.confirm(None).await?; + let pending_sends = w.get_pending_sends().await?; + t.check( + "an unresolved send exists before restart", + pending_sends.len() == 1, + format!("{} pending send(s), {stranded_amount} sats locked", pending_sends.len()), + )?; + // w drops here: wallet process gone, sqlite file left as it was. + } + + let w2 = file_wallet(&wallet_db, seed).await?; + let pending_after_restart = w2.get_pending_sends().await?; + t.check( + "the unresolved send survives the wallet restart", + pending_after_restart.len() == 1, + format!("{} pending send(s) after reopen", pending_after_restart.len()), + )?; + let report = w2.recover_incomplete_sagas().await?; + t.check( + "recovery reports a non-empty result", + !report.is_empty(), + format!( + "recovered {}, compensated {}, skipped {}, failed {}", + report.recovered, report.compensated, report.skipped, report.failed + ), + )?; + t.check("recovery failed nothing", report.failed == 0, format!("failed {}", report.failed))?; + + // Reclaim it explicitly. revoke_send swaps the proofs back — this is the call that actually + // returns value to the spendable set, which is what `check_all_pending_proofs` does NOT do. + // + // Whichever branch runs, the SAME closing contract is asserted below: full funded value back, + // nothing pending, nothing reserved. Neither branch is allowed a weaker oracle than the other. + let still_pending = w2.get_pending_sends().await?; + let balance_before_revoke = balance(&w2).await?; + let recovery_route; + if let Some(op) = still_pending.first().copied() { + let reclaimed = u64::from(w2.revoke_send(op).await?); + let balance_after_revoke = balance(&w2).await?; + recovery_route = "revoke_send"; + t.check( + "revoke_send reclaims the WHOLE stranded amount, not merely something", + reclaimed == stranded_amount, + format!("reclaimed {reclaimed} sats, {stranded_amount} was locked"), + )?; + t.check( + "spendable balance grows by exactly the reclaimed amount", + balance_after_revoke == balance_before_revoke + reclaimed, + format!("{balance_before_revoke} -> {balance_after_revoke} (+{reclaimed})"), + )?; + t.check( + "no unresolved sends remain", + w2.get_pending_sends().await?.is_empty(), + format!("{} left", w2.get_pending_sends().await?.len()), + )?; + } else { + // Recovery compensated it by itself: assert THAT outcome, held to the same value contract. + recovery_route = "compensated by recover_incomplete_sagas"; + t.check( + "recovery compensated the send without an explicit revoke", + report.compensated >= 1, + format!("compensated {}", report.compensated), + )?; + t.check( + "compensation alone already restored the stranded amount", + balance_before_revoke == RECOVER_FUND, + format!("spendable {balance_before_revoke}, funded {RECOVER_FUND}"), + )?; + } + + // The closing value contract, identical for both routes. input_fee_ppk is 0 on this fixture, so + // an issue + send + reclaim cycle is exactly value-preserving; that zero is ASSERTED from the + // mint's own active keyset above, not assumed from the config file, so if the fixture ever gains + // an input fee this leg fails loudly instead of quietly tolerating a shortfall. + let recovered_spendable = balance(&w2).await?; + let recovered_pending = u64::from(w2.total_pending_balance().await?); + let recovered_reserved = u64::from(w2.total_reserved_balance().await?); + t.check( + "the complete funded value is spendable again", + recovered_spendable == RECOVER_FUND, + format!("{recovered_spendable} sats spendable, funded {RECOVER_FUND} (via {recovery_route})"), + )?; + t.check( + "nothing is left pending or RESERVED after recovery", + recovered_pending == 0 && recovered_reserved == 0, + format!("pending {recovered_pending}, reserved {recovered_reserved}"), + )?; + // What check_all_pending_proofs ACTUALLY does, asserted rather than described: it returns the + // total of orphaned proofs still pending at the mint, and removes the spent ones. It does not + // move survivors back to Unspent (cdk 0.17.2 src/wallet/proofs.rs:121-180). + let still_pending_amount = u64::from(w2.check_all_pending_proofs().await?); + t.check( + "check_all_pending_proofs returns the residual pending total", + still_pending_amount == u64::from(w2.total_pending_balance().await?), + format!( + "returned {still_pending_amount}, wallet pending {}", + u64::from(w2.total_pending_balance().await?) + ), + )?; + + // ------------------------------------------------------------------ 8. mint restart + t.leg("mint restart: full residual value still spendable, nothing duplicated"); + let sender_pre = balance(&wallet).await?; + let receiver_pre = balance(&receiver).await?; + let recovery_pre = balance(&w2).await?; + let total_pre = sender_pre + receiver_pre + recovery_pre; + t.check( + "there is real residual value to test with", + total_pre > 0, + format!("sender {sender_pre} + receiver {receiver_pre} + recovery {recovery_pre} = {total_pre}"), + )?; + println!(" {}", test_mint(&["restart"])?.replace('\n', " | ")); + + // Now prove it against the MINT: sweep every wallet's whole balance into a fresh wallet. Local + // totals cannot lie about this, because the mint has to sign every swap. + // + // Every fee below is the fee CDK QUOTED for that send, and on this fixture it must be zero — the + // active keyset's `input_fee_ppk` was asserted to be 0 against the mint in the recovery leg, so + // this is a full-value contract, not a tolerance. Nothing here is permitted to explain a + // shortfall as a fee after the fact: that is precisely how the previous version could have + // called a sat stranded in Reserved a fee and passed. + let sink = memory_wallet().await?; + let mut swept = 0u64; + let mut quoted_fees = 0u64; + for (name, w) in [("sender", &wallet), ("receiver", &receiver), ("recovery", &w2)] { + let before = balance(w).await?; + match send_full_balance(w).await? { + Some(s) => { + t.check( + &format!("{name}: CDK quoted a zero fee, so the whole balance is sendable"), + s.quoted_fee == 0 && s.locked == s.before, + format!("locked {} of {} sats, quoted fee {}", s.locked, s.before, s.quoted_fee), + )?; + let got = u64::from(sink.receive(&s.token, ReceiveOptions::default()).await?); + t.check( + &format!("{name}: mint honoured the swept token after restart"), + got == s.locked, + format!("swept {got} of {} sats", s.before), + )?; + // Per-wallet completeness, with the fee taken from the quote rather than the gap. + t.check( + &format!("{name}: pre-sweep value == received + quoted fee"), + s.before == got + s.quoted_fee, + format!("{} == {got} + {}", s.before, s.quoted_fee), + )?; + swept += got; + quoted_fees += s.quoted_fee; + } + None => t.check( + &format!("{name}: nothing to sweep"), + before == 0, + format!("{before} sats but no sendable amount"), + )?, + } + } + let sink_balance = balance(&sink).await?; + t.check( + "swept value reconciles exactly with the pre-restart total", + sink_balance == swept && swept + quoted_fees == total_pre, + format!( + "sink {sink_balance} = swept {swept}; swept + quoted fees {quoted_fees} = {total_pre} pre-restart" + ), + )?; + t.check( + "the whole pre-restart total arrived, no fee was charged at all", + quoted_fees == 0 && sink_balance == total_pre, + format!("sink {sink_balance} of {total_pre} pre-restart, quoted fees {quoted_fees}"), + )?; + // Spendable, pending AND RESERVED. Omitting reserved was the hole: an abandoned prepare_send + // leaves value in a pool that neither of the other two queries can see (cdk 0.17.2 + // wallet/balance.rs:23-34 — pending and reserved are distinct proof sets). + for (name, w) in [("sender", &wallet), ("receiver", &receiver), ("recovery", &w2)] { + let left = balance(w).await?; + let pending = u64::from(w.total_pending_balance().await?); + let reserved = u64::from(w.total_reserved_balance().await?); + t.check( + &format!("{name} fully drained — spendable, pending and reserved all zero"), + left == 0 && pending == 0 && reserved == 0, + format!("spendable {left}, pending {pending}, reserved {reserved}"), + )?; + } + // No duplicate credit from a rolled-back database: the token spent before the restart is still + // burned, with the exact protocol error. + match sink.receive(&token_str, ReceiveOptions::default()).await { + Ok(a) => t.check( + "pre-restart spent token", + false, + format!("ACCEPTED {} sats after restart — duplicate credit!", u64::from(a)), + )?, + Err(e) => t.check( + "pre-restart spent token still fails with Error::TokenAlreadySpent", + is_already_spent(&e), + format!("{e:?}"), + )?, + } + + // ------------------------------------------------------------------ 9. loopback only + t.leg("loopback-only reachability"); + let addrs = listener_local_addrs()?; + t.check("a listener exists on 8085", !addrs.is_empty(), format!("{addrs:?}"))?; + let all_loopback = addrs.iter().all(|a| { + a.eq_ignore_ascii_case("0100007F") + || a.eq_ignore_ascii_case("00000000000000000000000001000000") + }); + t.check( + "every 8085 listener is bound to loopback", + all_loopback, + format!("/proc/net/tcp local_address {addrs:?}"), + )?; + match container_ip() { + Some(ip) => { + let target = SocketAddr::new(ip, MINT_PORT); + let reachable = TcpStream::connect_timeout(&target, Duration::from_millis(750)).is_ok(); + t.check( + "mint NOT reachable on the container's own routable address", + !reachable, + format!("{target} refused"), + )?; + } + None => bail!("could not determine a non-loopback container address; exclusion UNPROVEN"), + } + + // ------------------------------------------------------------------ 10. state isolation + // The mint's private state must not be inside the delivered workdir (advisor F3). The + // controller owns this check; the gate runs it so a single command covers it. + t.leg("private test state is outside the delivered workdir"); + let isolation = test_mint(&["isolation"])?; + for line in isolation.lines() { + println!(" | {line}"); + } + t.check( + "test-mint isolation reports state outside the delivery dir", + isolation.contains("state inside delivered dir: no"), + "see the lines above", + )?; + t.check( + "no mint state names appear in the delivered workdir", + !isolation.contains("FOUND:"), + "see the lines above", + )?; + + println!("\n====================================================="); + println!("PASS — {} assertions across {} legs", t.checks, t.legs.len()); + for (i, leg) in t.legs.iter().enumerate() { + println!(" {}. {leg}", i + 1); + } + println!("mint: {MINT_URL} (fakewallet, worthless test ecash)"); + println!("====================================================="); + Ok(()) +} diff --git a/docker/maxplayer-cashu-sandbox/bin/cashu-toolchain-check b/docker/maxplayer-cashu-sandbox/bin/cashu-toolchain-check new file mode 100755 index 000000000..fa4a6e30a --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/bin/cashu-toolchain-check @@ -0,0 +1,95 @@ +#!/bin/sh +# cashu-toolchain-check — prove this image carries a working OFFLINE Rust/CDK build environment. +# +# The advisor's F1 was that the first cut shipped precompiled binaries only: BuildKit cache mounts +# never become image layers, so "a dependency cache exists" was true of the build, not of the +# image. The answer to that is not a claim, it is a compile. This script copies the shipped example +# crate into a writable directory and builds it with `--offline --locked` against the baked +# registry. No network is used, and it fails loudly rather than falling back to a fetch. +# +# It deliberately does NOT build inside the delivered workdir: compiling there would put a target/ +# directory into the buyer's deliverable. +# +# PATH is set here rather than inherited. The image's ENV puts the toolchain on PATH, but a LOGIN +# shell discards that — Debian's /etc/profile assigns PATH unconditionally — so this script failed +# with `rustc: not found` under `bash -lc` in an image that was in fact complete. A proof script +# that reports a false negative depending on how its caller spawned a shell is not a proof, so it +# now resolves its own toolchain. +PATH="/opt/rust/toolchain/bin:/opt/rust/cargo/bin:$PATH" +export PATH +CARGO_HOME="${CARGO_HOME:-/opt/rust/cargo}" +export CARGO_HOME + +set -eu + +usage() { + cat <&2; usage >&2; exit 64 ;; +esac + +STATE_ROOT="${TEST_MINT_STATE_ROOT:-/var/lib/cashu-test-state}" +BUILD_ROOT="${STATE_ROOT}/toolchain-check" +SRC=/opt/cashu/examples/wallet-roundtrip + +echo "== toolchain identity" +rustc --version +cargo --version +echo "CARGO_HOME=${CARGO_HOME:-unset}" +echo "CARGO_NET_OFFLINE=${CARGO_NET_OFFLINE:-unset}" +cat /opt/rust/TOOLCHAIN.txt 2>/dev/null || true + +echo +echo "== dependency cache" +crates=$(find "${CARGO_HOME}/registry/cache" -name '*.crate' 2>/dev/null | wc -l | tr -d ' ') +echo "cached .crate archives: ${crates}" +if [ "${crates}" -lt 50 ]; then + echo "cashu-toolchain-check: dependency cache looks empty (${crates} archives) — F1 not satisfied" >&2 + exit 1 +fi +for want in cdk-0.17.2 cashu-0.17.2 cdk-sqlite-0.17.2; do + if find "${CARGO_HOME}/registry/cache" -name "${want}.crate" | grep -q .; then + echo " present: ${want}.crate" + else + echo "cashu-toolchain-check: ${want}.crate missing from the cache" >&2 + exit 1 + fi +done + +echo +echo "== offline build of ${SRC}" +rm -rf "${BUILD_ROOT}" +mkdir -p "${BUILD_ROOT}" +cp -a "${SRC}/." "${BUILD_ROOT}/" +cd "${BUILD_ROOT}" +test -f Cargo.lock || { echo "cashu-toolchain-check: example has no Cargo.lock" >&2; exit 1; } +# --offline AND --locked: no fetch, no resolution change. If the baked cache is missing anything the +# lockfile names, this fails here instead of quietly reaching the network. +cargo build --offline --locked --quiet +echo "built: ${BUILD_ROOT}/target/debug/wallet-roundtrip" +ls -l target/debug/wallet-roundtrip + +if [ "${RUN_IT}" = "yes" ]; then + echo + echo "== running it against the test mint" + test-mint status >/dev/null 2>&1 || test-mint start >/dev/null + FAKE_INVOICE="$(test-mint invoice 4000)" ./target/debug/wallet-roundtrip +fi + +echo +echo "PASS — offline toolchain + CDK cache verified in-image" diff --git a/docker/maxplayer-cashu-sandbox/bin/test-mint b/docker/maxplayer-cashu-sandbox/bin/test-mint new file mode 100755 index 000000000..54d965ed9 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/bin/test-mint @@ -0,0 +1,264 @@ +#!/bin/sh +# test-mint — start/stop/reset the sandbox-local fakewallet test mint. +# +# Worthless test ecash, loopback only. Reproducible: `reset` returns the mint to a known-empty +# state, `start` is idempotent, and nothing here touches a seller config or any host path. +# +# PRIVATE STATE LIVES OUTSIDE THE DELIVERED WORKDIR (advisor F3). The seller bind-mounts the HOST +# job workdir at /work (seller_exec.rs:843-849) and stages every nonignored file under it for +# delivery (seller_git.rs:335-354, whose only built-in exclusion is seller-run.jsonl). State kept +# under /work would therefore be handed to a buyer AND would survive `docker rm`, because removing +# a container does not delete a bind-mounted host directory. So the default root is +# /var/lib/cashu-test-state — container-local, world-writable, disposable with the container, and +# retained across a mint-process restart. +# +# The seed is a throwaway BIP-39 mnemonic generated at reset into a 0600 file and passed to +# cdk-mintd with --seed-file. It is never echoed, never an argument, never in this image. + +set -eu + +STATE_ROOT="${TEST_MINT_STATE_ROOT:-/var/lib/cashu-test-state}" +WORK="${TEST_MINT_WORK_DIR:-${STATE_ROOT}/mint}" +HOST="${TEST_MINT_HOST:-127.0.0.1}" +PORT="${TEST_MINT_PORT:-8085}" +URL="http://${HOST}:${PORT}/" +TEMPLATE="${TEST_MINT_TEMPLATE:-/opt/cashu/test-mint.config.toml}" +# The delivered workdir, only ever READ here, to prove state is not inside it. +DELIVERY_DIR="${TEST_MINT_DELIVERY_DIR:-/work}" + +CONFIG="${WORK}/config.toml" +SEED="${WORK}/seed" +PIDFILE="${WORK}/mintd.pid" +LOG="${WORK}/mintd.log" + +# Containment is the point of this rig, so it is enforced here rather than documented. +assert_loopback_host() { + case "$1" in + 127.0.0.1|::1|localhost) return 0 ;; + *) + echo "test-mint: refusing to bind '$1': loopback only (127.0.0.1, ::1)" >&2 + exit 2 + ;; + esac +} +assert_loopback_host "${HOST}" + +# ...and enforced on the RENDERED CONFIG too, not only on $TEST_MINT_HOST (advisor N1). An existing +# or hand-edited config.toml is reused as-is by design, so the bind address that will actually take +# effect is the one in the file. Refuse to start on a non-loopback listen_host whatever produced it. +assert_config_loopback() { + [ -s "${CONFIG}" ] || return 0 + listen=$(sed -n 's/^[[:space:]]*listen_host[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "${CONFIG}" | head -n 1) + if [ -n "${listen}" ]; then + assert_loopback_host "${listen}" + fi +} + +# State must not be inside the delivered workdir. Checked at every start, because +# TEST_MINT_WORK_DIR is an override and a wrong value here is exactly the defect F3 named. +assert_state_not_delivered() { + case "${WORK}/" in + "${DELIVERY_DIR}"/*) + echo "test-mint: refusing to put private mint state at ${WORK}: that is inside the delivered workdir ${DELIVERY_DIR}" >&2 + echo "test-mint: the seed, database and logs would be staged into the job deliverable." >&2 + exit 2 + ;; + esac +} +assert_state_not_delivered + +is_running() { + [ -f "${PIDFILE}" ] || return 1 + pid=$(cat "${PIDFILE}" 2>/dev/null || echo "") + [ -n "${pid}" ] || return 1 + kill -0 "${pid}" 2>/dev/null +} + +# node is in the base image (node:22-bookworm-slim); curl is not. /v1/info is the NUT-06 mint info +# endpoint, so a 200 here means the mint is actually serving the protocol, not merely listening. +probe() { + node -e ' + const url = process.argv[1]; + fetch(url, { signal: AbortSignal.timeout(2000) }) + .then(r => r.ok ? r.text().then(t => { process.stdout.write(t); process.exit(0); }) + : process.exit(1)) + .catch(() => process.exit(1)); + ' "http://${HOST}:${PORT}/v1/info" 2>/dev/null +} + +ensure_state() { + mkdir -p "${WORK}" + if [ ! -s "${SEED}" ]; then + old_umask=$(umask) + umask 077 + test-mint-seed > "${SEED}" + umask "${old_umask}" + chmod 0600 "${SEED}" + fi + if [ ! -s "${CONFIG}" ]; then + sed -e "s|__MINT_URL__|${URL}|g" \ + -e "s|__LISTEN_HOST__|${HOST}|g" \ + -e "s|__LISTEN_PORT__|${PORT}|g" \ + "${TEMPLATE}" > "${CONFIG}" + fi + assert_config_loopback +} + +cmd_start() { + if is_running; then + echo "test-mint: already running (pid $(cat "${PIDFILE}")) at ${URL}" + return 0 + fi + ensure_state + cdk-mintd --work-dir "${WORK}" --config "${CONFIG}" --seed-file "${SEED}" \ + >> "${LOG}" 2>&1 & + echo $! > "${PIDFILE}" + + i=0 + while [ "${i}" -lt 60 ]; do + if probe >/dev/null; then + echo "test-mint: up at ${URL} (pid $(cat "${PIDFILE}"), state ${WORK})" + return 0 + fi + if ! is_running; then + echo "test-mint: mintd exited during startup; last log lines:" >&2 + tail -n 20 "${LOG}" >&2 || true + return 1 + fi + i=$((i + 1)) + sleep 1 + done + echo "test-mint: timed out after 60s waiting for ${URL}v1/info; last log lines:" >&2 + tail -n 20 "${LOG}" >&2 || true + return 1 +} + +cmd_stop() { + if ! is_running; then + rm -f "${PIDFILE}" + echo "test-mint: not running" + return 0 + fi + pid=$(cat "${PIDFILE}") + kill "${pid}" 2>/dev/null || true + i=0 + while [ "${i}" -lt 20 ] && kill -0 "${pid}" 2>/dev/null; do + i=$((i + 1)) + sleep 1 + done + kill -9 "${pid}" 2>/dev/null || true + rm -f "${PIDFILE}" + echo "test-mint: stopped (pid ${pid})" +} + +# restart keeps the database and the seed: that is the "recovery/restart without lost or duplicate +# balance" case. reset throws both away and is the between-runs clean slate. +cmd_restart() { + cmd_stop + cmd_start +} + +cmd_reset() { + cmd_stop >/dev/null 2>&1 || true + # Bounded on purpose: only the resolved state dir, which has already been proven to sit outside + # the delivered workdir. `reset` refusing a path it does not own is cheaper than explaining a + # deleted deliverable (advisor N3). + case "${WORK}" in + /|/work|/work/*|/opt|/opt/*|/usr|/usr/*|"") + echo "test-mint: refusing to reset '${WORK}'" >&2 + exit 2 + ;; + esac + rm -rf "${WORK}" + echo "test-mint: reset (removed ${WORK})" +} + +cmd_status() { + if is_running && probe >/dev/null; then + echo "test-mint: healthy at ${URL} (pid $(cat "${PIDFILE}"))" + return 0 + fi + if is_running; then + echo "test-mint: process alive (pid $(cat "${PIDFILE}")) but ${URL}v1/info is not answering" + return 1 + fi + echo "test-mint: not running" + return 1 +} + +# The F3 evidence command: prints where private state lives, proves it is outside the delivered +# workdir, and lists whatever the mint has contributed to that workdir (which must be nothing). +cmd_isolation() { + echo "state root: ${STATE_ROOT}" + echo "mint state dir: ${WORK}" + echo "delivered dir: ${DELIVERY_DIR}" + printf 'state inside delivered dir: ' + case "${WORK}/" in + "${DELIVERY_DIR}"/*) echo "YES — THIS IS A DEFECT"; rc=1 ;; + *) echo "no"; rc=0 ;; + esac + if [ -d "${WORK}" ]; then + echo "state contents (names and sizes only, no values):" + find "${WORK}" -maxdepth 1 -mindepth 1 -exec sh -c 'printf " %s %s bytes\n" "$(basename "$1")" "$(wc -c < "$1" 2>/dev/null || echo dir)"' _ {} \; + printf 'seed mode: ' + [ -f "${SEED}" ] && stat -c '%a' "${SEED}" || echo "(no seed yet)" + else + echo "state dir does not exist yet" + fi + if [ -d "${DELIVERY_DIR}" ]; then + echo "delivered dir entries matching test-mint state:" + found=$(find "${DELIVERY_DIR}" -maxdepth 2 \ + \( -name '.test-mint' -o -name 'mintd.log' -o -name 'seed' -o -name 'cdk-mintd.sqlite*' \) \ + 2>/dev/null || true) + if [ -z "${found}" ]; then + echo " none" + else + echo "${found}" | sed 's/^/ FOUND: /' + rc=1 + fi + else + echo "delivered dir ${DELIVERY_DIR} not present (standalone run, no bind mount)" + fi + return "${rc}" +} + +case "${1:-help}" in + start) cmd_start ;; + stop) cmd_stop ;; + restart) cmd_restart ;; + reset) cmd_reset ;; + status) cmd_status ;; + isolation) cmd_isolation ;; + info) probe || { echo "test-mint: no answer at ${URL}v1/info" >&2; exit 1; } ;; + invoice) shift; test-mint-invoice "$@" ;; + url) echo "${URL}" ;; + state) echo "${WORK}" ;; + logs) tail -n "${2:-50}" "${LOG}" ;; + help|--help|-h) + cat <&2 + exit 64 + ;; +esac diff --git a/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.lock b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.lock new file mode 100644 index 000000000..ef64d3322 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.lock @@ -0,0 +1,3370 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[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 = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "async-compression" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f10dafd0c8d2e51ae9a748805777613ed0bbe17bf586b76c8311f45c020a32f" +dependencies = [ + "compression-codecs", + "compression-core", + "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.5", +] + +[[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 = "base58ck" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" +dependencies = [ + "bitcoin_hashes", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[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 = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.8", + "rand_core 0.6.4", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" +dependencies = [ + "base58ck", + "base64 0.21.7", + "bech32", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes", + "hex-conservative 0.2.3", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.3.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] + +[[package]] +name = "bitcoin-payment-instructions" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29f5b43b0e8338cd36ce7940bd6b8a007d54f1cbe25314ec3fbba566f870b085" +dependencies = [ + "bitcoin", + "lightning", + "lightning-invoice", +] + +[[package]] +name = "bitcoin-units" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" +dependencies = [ + "bitcoin-consensus-encoding", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.3", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[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 = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cashu" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e47bf3a30a044bfad3b1e222aa22d432c5895b28a743f68b8e338af4f12a01" +dependencies = [ + "bitcoin", + "cbor-diag", + "ciborium", + "lightning", + "lightning-invoice", + "once_cell", + "serde", + "serde_json", + "serde_with", + "strum", + "strum_macros", + "thiserror", + "tracing", + "unicode-normalization", + "url", + "uuid", + "web-time", + "zeroize", +] + +[[package]] +name = "cbor-diag" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc245b6ecd09b23901a4fbad1ad975701fd5061ceaef6afa93a2d70605a64429" +dependencies = [ + "bs58", + "chrono", + "data-encoding", + "half", + "nom", + "num-bigint", + "num-rational", + "num-traits", + "separator", + "url", + "uuid", +] + +[[package]] +name = "cc" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cdk" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911586800fc82527ebad109f25470715fd6e6a4f2c98d60cdf0cfed09ab5f75d" +dependencies = [ + "anyhow", + "arc-swap", + "async-trait", + "bitcoin", + "bitcoin-payment-instructions", + "cbor-diag", + "cdk-common", + "cdk-signatory", + "ciborium", + "futures", + "getrandom 0.2.17", + "gloo-timers", + "jsonwebtoken", + "lightning", + "lightning-invoice", + "regex", + "ring", + "rustls", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "url", + "uuid", + "web-time", + "zeroize", +] + +[[package]] +name = "cdk-common" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6157c494a479042c78ce995b62dc10e9b1fe15ba8dfd9fd03c43765f7f87a819" +dependencies = [ + "anyhow", + "async-trait", + "bitcoin", + "cashu", + "cbor-diag", + "cdk-http-client", + "ciborium", + "futures", + "getrandom 0.2.17", + "jsonwebtoken", + "lightning", + "lightning-invoice", + "parking_lot", + "paste", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tonic", + "tracing", + "url", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "cdk-http-client" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c808588b6d33f67c72b9e5d52661295166bff0661b38b281803a8eea6cf97f" +dependencies = [ + "futures", + "futures-channel", + "js-sys", + "regex", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio-tungstenite", + "tracing", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "cdk-signatory" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c024fd98d57f0d4ea22bf562b8e765b9a0e87199670de69a2b8a1d5d8b47f8c7" +dependencies = [ + "anyhow", + "async-trait", + "bip39", + "bitcoin", + "cdk-common", + "clap", + "getrandom 0.2.17", + "home", + "rustls", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cdk-sql-common" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816a5cb0461dbf32b042d14dd0fe44f788a75bdee9e78830ad9e6d35654bbd6d" +dependencies = [ + "async-trait", + "bitcoin", + "cdk-common", + "lightning-invoice", + "once_cell", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "cdk-sqlite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab7a6a12ed31086575404514b03729295690ce5d14ec7a55f9b2ab34d45a425" +dependencies = [ + "async-trait", + "bitcoin", + "cdk-common", + "cdk-sql-common", + "lightning-invoice", + "paste", + "rusqlite", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[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.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 = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "compression-codecs" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a6d0db8759036a783bc7c3f7a07f8cef3bf9470eb1db3bc86e8bcd1c5d0fe8" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414" + +[[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 = "crc32fast" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.5", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[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.5", +] + +[[package]] +name = "dnssec-prover" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9468f1a08c50bd1e5ad91b151e11ce8e806f8fa1c1eb9b07f66c7011de45a2e" +dependencies = [ + "bitcoin_hashes", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[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 = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[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 = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[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-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[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-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[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 = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[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 = "hex-conservative" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[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 = "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", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[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 = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[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.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[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 = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lightning" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab16d2a714c0b26d7230bd388ac383a30fce231c8927c62752afc0471a36dc6" +dependencies = [ + "bech32", + "bitcoin", + "dnssec-prover", + "hashbrown 0.13.2", + "libm", + "lightning-invoice", + "lightning-macros", + "lightning-types", + "possiblyrandom", +] + +[[package]] +name = "lightning-invoice" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d83bd798e04ab9eecc8bbef1fa17d3808859bcdc0406bd16c55d51c8834444" +dependencies = [ + "bech32", + "bitcoin", + "lightning-types", + "serde", +] + +[[package]] +name = "lightning-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c717494cdc2c8bb85bee7113031248f5f6c64f8802b33c1c9e2d98e594aa71" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "lightning-types" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77c676d4a34cceb2ae3756916e446b4d17f9430a24107e099981f0f9aec77e6" +dependencies = [ + "bitcoin", +] + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[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 = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[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 = "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-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[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 = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[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 = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "possiblyrandom" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c564dbf654befd49035528299f1208a40508f6e07efb11c163444e304e4484f" +dependencies = [ + "getrandom 0.2.17", +] + +[[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 = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "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 = [ + "libc", + "rand_chacha 0.3.1", + "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 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[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 2.13.1", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[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.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[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 = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.13.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustls" +version = "0.23.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" +dependencies = [ + "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-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "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 = "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 = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.8", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.1", + "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 = "separator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" + +[[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_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.5", +] + +[[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_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 = "serde_with" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" +dependencies = [ + "base64 0.23.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.2", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[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 = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" + +[[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 = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +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 = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[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.5", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[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 = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "base64 0.22.1", + "bytes", + "http", + "http-body", + "http-body-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + +[[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", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "async-compression", + "bitflags 2.13.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tokio", + "tokio-util", + "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 = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[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 = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wallet-roundtrip" +version = "0.1.0" +dependencies = [ + "anyhow", + "cdk", + "cdk-sqlite", + "rand 0.9.5", + "tokio", +] + +[[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.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" +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-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[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.5", +] + +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf06bd8162af0734b344780deb55b42a2429ae430870d13fcc12f238e880fe6e" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae42c0555055784c70058d19ba8e275528e8a99a706684868ace5da4e716a4ab" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.toml b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.toml new file mode 100644 index 000000000..d4791df30 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "wallet-roundtrip" +version = "0.1.0" +edition = "2021" +publish = false + +# A tested example (brief item C) that doubles as the runtime-toolchain proof (advisor F1): it is +# NOT prebuilt into the image. `cashu-toolchain-check` copies this crate into a writable dir and +# compiles it with `cargo build --offline --locked`, so a successful run proves the image really +# carries a Rust toolchain and a CDK dependency cache rather than only a precompiled binary. +[dependencies] +cdk = { version = "=0.17.2", default-features = false, features = ["wallet"] } +cdk-sqlite = "=0.17.2" +# Ranged here, EXACT in the committed Cargo.lock — `--locked` is what fixes the graph, and pinning +# both places would only add a second thing to keep in step. +tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] } +rand = "0.9" +anyhow = "1" + +[[bin]] +name = "wallet-roundtrip" +path = "src/main.rs" + +# Standalone crate root. These image-local crates are deliberately NOT members of the +# product workspace: adding them there would drag image tooling into every product build, and +# an empty [workspace] table is the supported way to say "this is its own root". +[workspace] diff --git a/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/src/main.rs b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/src/main.rs new file mode 100644 index 000000000..0ad5fd666 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/examples/wallet-roundtrip/src/main.rs @@ -0,0 +1,77 @@ +//! wallet-roundtrip — the smallest useful CDK 0.17.2 wallet program, against the sandbox-local +//! fakewallet mint. Worthless test ecash only. +//! +//! Compiled from source inside the job container by `cashu-toolchain-check`, offline. Its job is +//! twofold: show a specialist the shortest correct issue → send → receive → melt sequence at this +//! exact CDK version, and prove the image's Rust toolchain and dependency cache actually work. + +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; + +use anyhow::{anyhow, Result}; +use cdk::nuts::nut00::ProofsMethods; +use cdk::nuts::{CurrencyUnit, PaymentMethod}; +use cdk::wallet::{ReceiveOptions, SendOptions, Wallet}; +use cdk::Amount; +use cdk_sqlite::wallet::memory; +use rand::random; + +const MINT_URL: &str = "http://127.0.0.1:8085"; + +async fn wallet() -> Result { + let store = Arc::new(memory::empty().await?); + Ok(Wallet::new(MINT_URL, CurrencyUnit::Sat, store, random::<[u8; 64]>(), None)?) +} + +#[tokio::main] +async fn main() -> Result<()> { + let alice = wallet().await?; + let bob = wallet().await?; + + // 1. Mint info proves we are talking to a mint at all. + let info = alice.fetch_mint_info().await?.ok_or_else(|| anyhow!("no mint info"))?; + println!("mint: {:?}", info.version.map(|v| v.to_string())); + + // 2. Issue. Fakewallet auto-settles the quote, so this returns without any payment. + let quote = alice.mint_quote(PaymentMethod::BOLT11, Some(Amount::from(32)), None, None).await?; + let proofs = alice + .wait_and_mint_quote(quote, Default::default(), Default::default(), Duration::from_secs(30)) + .await?; + println!("issued: {} sats", u64::from(proofs.total_amount()?)); + + // 3. Send. prepare_send reserves proofs; the debit is final only at confirm. + let prepared = alice.prepare_send(Amount::from(8), SendOptions::default()).await?; + let token = prepared.confirm(None).await?; + println!("alice after send: {} sats", u64::from(alice.total_balance().await?)); + + // 4. Receive into a different wallet. + let got = bob.receive(&token.to_string(), ReceiveOptions::default()).await?; + println!("bob received: {} sats", u64::from(got)); + + // 5. Melt. The invoice is a fake one the mint's own backend will settle. + // A real specialist task would build this from a genuine BOLT11 string. + let quote = bob + .melt_quote(PaymentMethod::BOLT11, cdk_fake_invoice(4_000), None, None) + .await?; + let melt = bob.prepare_melt("e.id, HashMap::new()).await?; + let done = melt.confirm().await?; + println!( + "bob melted: state={:?} amount={} fee={}", + done.state(), + u64::from(done.amount()), + u64::from(done.fee_paid()) + ); + println!("bob final: {} sats", u64::from(bob.total_balance().await?)); + + Ok(()) +} + +/// A fakewallet-settleable BOLT11 string, read from the environment so this example does not need +/// `cdk-fake-wallet` as a dependency. `cashu-toolchain-check` fills it in with +/// `test-mint invoice `. +fn cdk_fake_invoice(msat: u64) -> String { + std::env::var("FAKE_INVOICE").unwrap_or_else(|_| { + panic!("set FAKE_INVOICE to a fakewallet invoice for {msat} msat (see: test-mint invoice)") + }) +} diff --git a/docker/maxplayer-cashu-sandbox/seat/memory/MEMORY.md b/docker/maxplayer-cashu-sandbox/seat/memory/MEMORY.md new file mode 100644 index 000000000..1f0cda392 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seat/memory/MEMORY.md @@ -0,0 +1,58 @@ +# Cashu / CDK specialist — index + +I am a Cashu and CDK specialist seat. My work is **wallet integration and protocol debugging in +Rust against CDK**. I am not a production mint operator: I run a fake mint to test against, and +that is the only mint I operate. + +Everything I need is already on this container's disk. Read it before searching the web — the disk +copy is pinned and exact, the web is neither. + +## My topic files — read them with `cat`, at these paths + +Only this index reaches my prompt; the topics do not. They are baked into the job image, so I open +them by absolute container path. `[[wikilinks]]` would not resolve here — the seat home that holds +the host copies is deliberately not mounted into a job. + +- `/opt/cashu/knowledge/cashu-corpus.md` — where the spec and the CDK source live on disk, their + pins, and how to search them without drowning. +- `/opt/cashu/knowledge/test-mint.md` — the sandbox-local fakewallet mint: start, stop, reset, where + its private state lives, and the rules that keep it worthless and unreachable. +- `/opt/cashu/knowledge/cdk-wallet-api.md` — the CDK 0.17.2 wallet calls that actually exist, with + the ones whose names and semantics are easy to get wrong. +- `/opt/cashu/knowledge/known-drift.md` — places where upstream documentation is WRONG at our + pinned version. Measured, each with the source line that settles it. + +## Where everything is + +| what | path | +|---|---| +| my topic files | `/opt/cashu/knowledge/` | +| Cashu NUTs + CDK source, pinned | `/opt/cashu/corpus/` (`PINS.txt` names both commits) | +| Rust toolchain + warm CDK crate cache | `/opt/rust/`, `CARGO_HOME=/opt/rust/cargo` | +| runnable example, as source | `/opt/cashu/examples/wallet-roundtrip/` | +| test mint control | `test-mint` (see its `help`) | +| private test state — NOT delivered | `/var/lib/cashu-test-state/` | + +## I can compile offline + +`CARGO_NET_OFFLINE=true` is set and the CDK dependency graph is already cached, so +`cargo build --offline --locked` works with no network. `cashu-toolchain-check --run` proves it end +to end. If a build wants a crate that is not cached it FAILS rather than fetching — that is +deliberate, and the fix is to say so, not to switch the flag off silently. + +## Pins — state these dates when I quote a spec + +- **CDK 0.17.2** — the version this product pins (`cashu`, `cdk`, `cdk-sqlite`, all `=0.17.2`). + Corpus at tag `v0.17.2` = `6132607495ae0741e412a63f2acc34e4ccddfc55`. +- **NUTs** — `cashubtc/nuts` @ `49a909ce4d0739824b3859d4b3da21e6c1abdaeb`, **2026-08-23**. +- **Toolchain** — rustc/cargo 1.98.1; `/opt/rust/TOOLCHAIN.txt` has the exact strings. + +A baked corpus is stale from the moment it is baked. If a job turns on whether a NUT changed +recently, I say the pin date out loud and check upstream rather than quoting the disk as current. + +## How I answer + +Read the source, not my memory of the source. Every claim about CDK behaviour cites a file and +line from `/opt/cashu/corpus/cdk` or the crate source. If I did not run it, I say I did not run it. +Never write the test mint into a seller's `accepted_mints`, and never put mint state, seeds or logs +under `/work`. diff --git a/docker/maxplayer-cashu-sandbox/seat/memory/cashu-corpus.md b/docker/maxplayer-cashu-sandbox/seat/memory/cashu-corpus.md new file mode 100644 index 000000000..a486037be --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seat/memory/cashu-corpus.md @@ -0,0 +1,40 @@ +# cashu-corpus — the spec and CDK source on disk + +## Where + +- `/opt/cashu/corpus/nuts/` — the Cashu NUTs, `cashubtc/nuts` @ `49a909ce4d0739824b3859d4b3da21e6c1abdaeb` + (2026-08-23). One markdown file per NUT: `00.md`, `01.md`, … Read `README.md` first for the index + of which NUT is which. +- `/opt/cashu/corpus/cdk/` — the full `cashubtc/cdk` source at tag `v0.17.2` + (`6132607495ae0741e412a63f2acc34e4ccddfc55`). This is the authoritative answer to "what does CDK + actually do", because it is the exact code the product links. +- `/opt/cashu/corpus/PINS.txt` — the two commits above, so I can quote them without guessing. + +`.git` was removed from both at image build time. The corpus cannot be updated from inside a job, +and it does not drift underneath me mid-job. + +## Grep discipline + +The CDK tree is large. Searching it badly wastes a whole turn. + +1. **Name the NUT first.** Protocol questions are answered by `/opt/cashu/corpus/nuts/NN.md`, which + is short. Go there before the code. +2. **Then find the type, not the phrase.** `grep -rn "pub struct MeltQuote" /opt/cashu/corpus/cdk/crates` + beats grepping for "melt quote". +3. **Scope to a crate.** `crates/cdk/src/wallet/` for wallet behaviour, `crates/cdk/src/mint/` for + mint behaviour, `crates/cashu/src/nuts/` for the wire types, `crates/cdk-fake-wallet/` for the + test backend. Never grep from `/opt/cashu/corpus/cdk` root when I already know the crate. +4. **Read the tests.** `crates/cdk-integration-tests/` shows working call sequences at this exact + version — usually faster than reconstructing one from signatures. +5. **Examples are runnable answers.** The `cdk` crate's `examples/` directory has `mint-token.rs`, + `melt-token.rs`, `receive-token.rs`, `p2pk.rs`, `restore-wallet.rs` and more, all valid at + 0.17.2. + +Keep output small: pipe through `head`, and grep for a symbol rather than dumping a file. A file +over ~500 lines gets `sed -n 'START,ENDp'` after I know the line from grep. + +## What is NOT here + +No rendered rustdoc and no cashudevkit.org mirror. If I want API docs, the source *is* the docs — +doc comments are in the same files. If a question genuinely needs the website, I say so rather than +inventing what it says. diff --git a/docker/maxplayer-cashu-sandbox/seat/memory/cdk-wallet-api.md b/docker/maxplayer-cashu-sandbox/seat/memory/cdk-wallet-api.md new file mode 100644 index 000000000..b7376599f --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seat/memory/cdk-wallet-api.md @@ -0,0 +1,108 @@ +# cdk-wallet-api — CDK 0.17.2 wallet calls that exist + +Verified by compiling against `=0.17.2`. Names in this family are easy to guess wrong; these are +the ones that are real. + +## Construction + +```rust +let store = Arc::new(cdk_sqlite::wallet::memory::empty().await?); // or a file store +let wallet = Wallet::new(mint_url, CurrencyUnit::Sat, store, seed_bytes /* [u8; 64] */, None)?; +``` + +## Issue + +```rust +let quote = wallet.mint_quote(PaymentMethod::BOLT11, Some(Amount::from(64)), None, None).await?; +let proofs = wallet + .wait_and_mint_quote(quote, Default::default(), Default::default(), Duration::from_secs(30)) + .await?; +let minted = proofs.total_amount()?; // needs `use cdk::nuts::nut00::ProofsMethods;` +``` + +Against the fakewallet mint the quote auto-settles, so `wait_and_mint_quote` returns promptly. + +## Send / receive + +```rust +let prepared = wallet.prepare_send(Amount::from(21), SendOptions::default()).await?; +let fee = prepared.fee(); +let token = prepared.confirm(None).await?; // consumes `prepared` +let amount = other_wallet.receive(&token.to_string(), ReceiveOptions::default()).await?; +``` + +`prepare_send` reserves proofs; the debit is not final until `confirm`. + +## Melt + +```rust +let quote = wallet.melt_quote(PaymentMethod::BOLT11, invoice_string, None, None).await?; +let prepared = wallet.prepare_melt("e.id, HashMap::new()).await?; +let finalized = prepared.confirm().await?; // or .confirm_prefer_async() -> MeltOutcome +finalized.state(); finalized.amount(); finalized.fee_paid(); +``` + +`confirm_prefer_async()` returns `MeltOutcome::{Paid, Pending}`; a `Pending` can be awaited +directly, or resolved later with `wallet.finalize_pending_melts()` / +`wallet.check_melt_quote_status(&id)`. + +## Balance — three separate pools, not two + +Read from cdk 0.17.2 `src/wallet/balance.rs:7-40`. These are different queries over different +proof states, and conflating any two of them will make an accounting assertion pass while value is +stranded: + +- `wallet.total_balance()` — **spendable only**: `get_balance(..., Some(vec![State::Unspent]))`. +- `wallet.total_pending_balance()` — total of `get_pending_proofs()`. +- `wallet.total_reserved_balance()` — total of `get_reserved_proofs()`. A **distinct** pool from + pending. A conservation check that looks at spendable and pending but not reserved cannot see + value stranded in reserve, which is exactly what an abandoned `prepare_send` leaves behind. + +## Recovery — what actually returns value, and what does not + +`wallet.check_all_pending_proofs()` is **not** a recovery call. Read the body +(cdk 0.17.2 `src/wallet/proofs.rs:112-185`): it skips saga-managed proofs, asks the mint about the +rest, deletes the ones the mint reports **spent**, and returns the **total still pending**. It does +not move survivors back to `Unspent`, and its return value is a residual, not a reclaim. Asserting +`check_all_pending_proofs() > 0` proves nothing was recovered; it proves the opposite. + +The real path, three calls: + +- `wallet.recover_incomplete_sagas()` (`src/wallet/recovery.rs:302`) — resumes persisted sagas. + Returns `RecoveryReport { recovered, compensated, skipped, failed }` with `is_empty()`. An + interrupted operation is either driven forward (`recovered`) or rolled back (`compensated`); both + are success, and which one happens depends on how far the saga got. +- `wallet.get_pending_sends()` — operation ids for sends sitting in `SendSagaState::TokenCreated`, + i.e. a token was minted for a recipient who never redeemed it. +- `wallet.revoke_send(operation_id)` (`src/wallet/send/mod.rs:275`) — **this** is the reclaim: it + swaps those proofs back and returns the reclaimed `Amount`. + +So after a send that was never redeemed: `recover_incomplete_sagas()`, then for each id from +`get_pending_sends()` call `revoke_send(id)`. Verify by expected **value**, not by "an error did not +happen": the funded total must come back whole net of fees actually charged, with pending **and** +reserved both at zero. A partial reclaim satisfies `reclaimed > 0` and is still a loss. + +Scope note, so this is not read as more than it is: the seat's gate proves recovery across a +**wallet-object and store reopen** — the wallet is dropped and the same sqlite file is reopened in +the same process. That is not a proof of recovery after an OS-level process crash, after seed-only +restore with no local database, of an interrupted **melt**, or of a crash at every intermediate +saga stage. + +- Also available: `wallet.get_pending_proofs()`, `wallet.get_pending_spent_proofs()`, + `wallet.get_reserved_proofs()`. + +## Mint info — the name trap + +It is **`wallet.fetch_mint_info()`** (returns `Result, Error>`), not +`get_mint_info` — that name exists only on `AuthWallet`. There is also `wallet.load_mint_info()` +returning `MintInfo` directly. + +There is no `reclaim_unspent_proofs()`. Do not substitute `check_all_pending_proofs()` for it — that +returns a residual and reclaims nothing (see the recovery section above). The reclaim is +`revoke_send(operation_id)`. + +## Failure signatures worth recognising + +- Receiving an already-spent token: `Error` displaying **`Token Already Spent`**. +- A melt whose backend refuses: the wallet call errors with **`Payment failed`**. A melt can also + come back `Ok` with a non-`Paid` state — assert on the state, not merely on `is_ok()`. diff --git a/docker/maxplayer-cashu-sandbox/seat/memory/known-drift.md b/docker/maxplayer-cashu-sandbox/seat/memory/known-drift.md new file mode 100644 index 000000000..ee386d625 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seat/memory/known-drift.md @@ -0,0 +1,69 @@ +# known-drift — where upstream docs are wrong at our pin + +Each of these cost real time on 2026-09-09 and each is settled by a source line, not by memory. +When a doc and the pinned source disagree, **the source wins**. + +## 1. `cdk-mintd` config: the section is `[ln]`, not `[payment_backend]` + +Notes circulating internally describe `[payment_backend] backend = "fakewallet"` and +`CDK_MINTD_LN_BACKEND=fakewallet`. Neither is the 0.17.2 schema. + +Real shape (`cdk-mintd-0.17.2/src/config.rs:1002` `struct Settings`; +`example.config.toml:116-119`, `:283`): + +```toml +[ln] +ln_backend = "fakewallet" +unit = "sat" + +[fake_wallet] +fee_percent = 0.02 +reserve_fee_min = 1 +``` + +`ln` deserializes as `Vec` via an untagged `LnOneOrMany`, so `[ln]` (one) and `[[ln]]` +(per-unit) are both valid. Duplicate `(unit, method)` pairs are rejected at startup. + +## 2. `[ln]` min/max are REQUIRED, though the example comments them out + +`example.config.toml` shows `min_mint`, `max_mint`, `min_melt`, `max_melt` commented out. But +`struct Ln` (`src/config.rs:170-179`) gives them **no** `#[serde(default)]`. Omit any one and the +whole config fails with: + +``` +data did not match any variant of untagged enum LnOneOrMany for key `ln` +``` + +— an error that names the enum and not the missing field, and points nowhere near the cause. All +four must be present. `impl Default for Ln` uses 1 / 500000 / 1 / 500000. + +## 3. `cdk-mintd` needs `protoc` at build time even with `--no-default-features` + +`cdk-mintd` depends on `cdk-signatory` unconditionally, and that crate's `build.rs:14` compiles +`src/proto/signatory.proto` regardless of the `grpc` feature. Without `protobuf-compiler` the build +panics with "Could not find `protoc`". Nothing in the feature flags avoids it. + +## 4. `pay_err` alone does NOT simulate a failed payment + +`cdk-fake-wallet-0.17.2/src/lib.rs:706-714` inserts `check_payment_state` into its payment-states +map **before** it honours `pay_err`. So with `FakeInvoiceDescription::default()`'s `Paid` and only +`pay_err: true`, `make_payment` errors, the mint re-checks the payment status, sees `PAID`, and the +melt finalises `state=Paid`. Measured exactly that: `state=Paid, amount=5, fee_paid=0` for an +invoice the backend refused. + +A genuine failure needs **both**: + +```rust +FakeInvoiceDescription { + pay_invoice_state: MeltQuoteState::Unpaid, + check_payment_state: MeltQuoteState::Unpaid, + pay_err: true, + check_err: false, +} +``` + +Then the wallet call errors with `Payment failed` and the balance is unchanged. + +The general lesson, worth more than the four items: a fake backend that reports success when asked +the wrong way will make a test suite green without testing anything. Assert on amounts and states, +never on "the call returned". diff --git a/docker/maxplayer-cashu-sandbox/seat/memory/test-mint.md b/docker/maxplayer-cashu-sandbox/seat/memory/test-mint.md new file mode 100644 index 000000000..9b93f37a0 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seat/memory/test-mint.md @@ -0,0 +1,69 @@ +# test-mint — the sandbox-local fakewallet mint + +A real `cdk-mintd 0.17.2` speaking the real protocol, backed by a **fake** payment backend. The +ecash it issues is worthless. It exists so I can test wallet code against a mint that always +behaves, instantly, offline. + +## Commands + +```sh +test-mint start # start, wait until /v1/info answers; idempotent +test-mint status # exit 0 only if it is serving /v1/info +test-mint info # the NUT-06 mint info document +test-mint restart # stop + start, KEEPING the database (restart-recovery testing) +test-mint reset # stop and delete the work dir entirely (clean slate) +test-mint logs [n] # last n lines +test-mint url # http://127.0.0.1:8085/ +``` + +State lives in `$TEST_MINT_WORK_DIR`, default **`/var/lib/cashu-test-state/mint`** — container-local +and deliberately **outside `/work`**. `/work` is the delivered workdir: it is bind-mounted from the +host and everything in it is handed to the buyer, so mint databases, logs, config and the seed file +must never land there. `test-mint` refuses to start if its state root resolves inside the delivery +directory, and `test-mint isolation` is the command that shows this (state root, whether it is +inside the delivered dir, state file names and sizes, seed mode, and any delivered-dir entry +matching mint state). + +The state root is container-local, so it dies with the container either way. `reset` between test +runs; `restart` when I am specifically testing that balances survive a mint bounce (it keeps the +database and the seed). + +The seed is written to a `0600` file under that root and passed to `cdk-mintd` as `--seed-file`. +Never echo it, never pass it as an argument value, never copy it into `/work`. + +## Acceptance harness + +`mint-acceptance` is baked in and needs no build step: + +```sh +test-mint start && mint-acceptance +``` + +It runs eight legs — mint info, issue, send/receive, double-spend rejection, melt, failed payment, +restart recovery, loopback-only reachability — and asserts amounts, not statuses. It exits non-zero +on any failure and prints an assertion count. Read `/usr/local/bin/mint-acceptance`'s source in the +repo (`docker/maxplayer-cashu-sandbox/acceptance/src/main.rs`) for worked examples of every one of +those flows at CDK 0.17.2. + +## Rules I do not break + +- **This mint never goes in a seller's `accepted_mints`.** That set is the seller's real-money + surface; `payment_wallet.rs` checks it twice (realized mint, and the NUT-18 payload mint). Test + ecash in that set means the seat would accept worthless tokens as payment. +- **Loopback only.** It binds `127.0.0.1:8085`; `test-mint` refuses any non-loopback bind. Verified + from `/proc/net/tcp` (`local_address` `0100007F`), and the container's own bridge address refuses + the connection. +- **No real funds, ever.** The binary is built `--no-default-features --features fakewallet,sqlite`, + so cln/lnd/lnbits/bdk/ldk-node are not compiled in. It *cannot* be pointed at a real backend. +- The seed is a throwaway BIP-39 mnemonic generated per `reset` into a `0600` file. Never print it, + never put it in a deliverable, never pass it on a command line. + +## Simulating failures + +`cdk-fake-wallet` reads the BOLT11 **description** as a `FakeInvoiceDescription` JSON blob: +`pay_invoice_state`, `check_payment_state`, `pay_err`, `check_err`. Build the invoice with +`cdk_fake_wallet::create_fake_invoice(amount_msat, description_json)`. + +To make a payment genuinely fail I must set **both** `pay_err: true` **and** +`check_payment_state: Unpaid` — see [[known-drift]]. Setting only `pay_err` yields a melt that +finalises as `Paid`. diff --git a/docker/maxplayer-cashu-sandbox/seedgen/Cargo.lock b/docker/maxplayer-cashu-sandbox/seedgen/Cargo.lock new file mode 100644 index 000000000..08f35e3dd --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seedgen/Cargo.lock @@ -0,0 +1,234 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "bip39" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +dependencies = [ + "bitcoin_hashes", + "rand", + "rand_core", + "serde", + "unicode-normalization", +] + +[[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 = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hex-conservative" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3fef046dca3ca91ee1408a8c1b80ab777e80a4d308d1bf4e7adb3fcb047e08" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[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 = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "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.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test-mint-seed" +version = "0.1.0" +dependencies = [ + "bip39", +] + +[[package]] +name = "tinyvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "zerocopy" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] diff --git a/docker/maxplayer-cashu-sandbox/seedgen/Cargo.toml b/docker/maxplayer-cashu-sandbox/seedgen/Cargo.toml new file mode 100644 index 000000000..6c9e177d5 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seedgen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "test-mint-seed" +version = "0.1.0" +edition = "2021" +publish = false + +# Was an inline heredoc in the Dockerfile with a ranged dependency and no lockfile (advisor F6). +# Now a real crate with a committed Cargo.lock, installed --locked, so the seed generator's +# dependency graph is fixed at the resolution recorded here. +[dependencies] +bip39 = { version = "=2.2.2", features = ["rand"] } + +[[bin]] +name = "test-mint-seed" +path = "src/main.rs" + +# Standalone crate root. These image-local crates are deliberately NOT members of the +# product workspace: adding them there would drag image tooling into every product build, and +# an empty [workspace] table is the supported way to say "this is its own root". +[workspace] diff --git a/docker/maxplayer-cashu-sandbox/seedgen/src/main.rs b/docker/maxplayer-cashu-sandbox/seedgen/src/main.rs new file mode 100644 index 000000000..06e567f11 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/seedgen/src/main.rs @@ -0,0 +1,10 @@ +//! test-mint-seed — print one fresh BIP-39 mnemonic on stdout, nothing else. +//! +//! `cdk-mintd` has no seed auto-generation: `lib.rs:1238` bails with "No seed nor remote signatory +//! set", and `--seed-file` is parsed as a BIP-39 mnemonic. The `test-mint` controller redirects this +//! output straight into a 0600 file under a umask of 077 and never echoes it. + +fn main() { + let mnemonic = bip39::Mnemonic::generate(12).expect("generate mnemonic"); + println!("{mnemonic}"); +} diff --git a/docker/maxplayer-cashu-sandbox/test-mint.config.toml b/docker/maxplayer-cashu-sandbox/test-mint.config.toml new file mode 100644 index 000000000..c94077310 --- /dev/null +++ b/docker/maxplayer-cashu-sandbox/test-mint.config.toml @@ -0,0 +1,66 @@ +# cdk-mintd 0.17.2 — sandbox-local FAKEWALLET test mint. +# +# WORTHLESS TEST ECASH. This mint has no Lightning backend and no onchain backend that can move +# real value: it is built --no-default-features --features fakewallet,sqlite, so the cln / lnd / +# lnbits / bdk / ldk-node backends are not compiled in at all. Quotes auto-settle after a short +# fake delay. Never put this mint's URL in a seller's `accepted_mints`. +# +# Schema verified against cdk-mintd 0.17.2 sources (example.config.toml:116-119 for [ln], +# :283 for [fake_wallet], src/config.rs:1002 `struct Settings`). Note this is NOT the +# `[payment_backend] backend = "fakewallet"` shape some older notes describe. +# +# `test-mint` renders the __PLACEHOLDER__ values and writes the result into the job workdir; the +# seed is supplied separately via --seed-file and never appears here. + +[info] +url = "__MINT_URL__" +listen_host = "__LISTEN_HOST__" +listen_port = __LISTEN_PORT__ +# input_fee_ppk left at the default (0) so test arithmetic is exact unless a test opts into fees. + +[info.quote_ttl] +mint_ttl = 600 +melt_ttl = 120 + +[info.logging] +# Console only. The mint writes no log file into the job workdir, so a job's deliverable cannot +# accidentally carry mint logs. +output = "stderr" +console_level = "info" + +[info.http_cache] +backend = "memory" +ttl = 60 +tti = 60 + +[mint_info] +name = "maxplayer sandbox test mint" +description = "Worthless test ecash. Fakewallet backend, loopback only." + +[database] +engine = "sqlite" + +# Single fakewallet backend on sat. Repeat as [[ln]] with a different `unit` to exercise +# multi-unit; duplicate (unit, method) pairs are rejected at startup. +# min_mint/max_mint/min_melt/max_melt are REQUIRED here, even though cdk-mintd's own +# example.config.toml shows them commented out: `struct Ln` (src/config.rs:170-179) gives them no +# #[serde(default)], so omitting any one of them fails the whole file with the unhelpful +# "data did not match any variant of untagged enum LnOneOrMany for key `ln`". Measured: that is +# exactly how the first run of this rig failed. Values below mirror `impl Default for Ln`. +[ln] +ln_backend = "fakewallet" +unit = "sat" +min_mint = 1 +max_mint = 500000 +min_melt = 1 +max_melt = 500000 + +[fake_wallet] +# Non-zero so melt fee-reserve behaviour is exercised rather than silently skipped. +fee_percent = 0.02 +reserve_fee_min = 1 +custom_payment_methods = [] +# Short but non-zero: a quote that settles instantly hides pending-state bugs, and a long delay +# makes the acceptance run slow. 1-3s is the upstream example's own range. +min_delay_time = 1 +max_delay_time = 3 diff --git a/reports/cashu-cdk-seller/AUTH-PROVISIONING.md b/reports/cashu-cdk-seller/AUTH-PROVISIONING.md new file mode 100644 index 000000000..3f80571ad --- /dev/null +++ b/reports/cashu-cdk-seller/AUTH-PROVISIONING.md @@ -0,0 +1,153 @@ +# Protected-auth provisioning for the cashu-cdk-seller seat + +The seat builds, boots and passes `doctor` with 17 checks, and then **refuses to advertise**. That +refusal is correct behaviour, not a defect, and this document is the exact supported path out of it. + +Everything below was read from this repository at commit `aa8e831`'s tree, not recalled. Nothing +here required reading, copying, or holding a secret value, and none is included. + +## What the seat is actually blocked on + +Observed at boot: + +``` +pre-advertise probe FAILED claude: ... {"code":-32000,"message":"Authentication required"} +prove-before-advertise: none of 1 configured harness(es) produced a probe artifact; refusing to advertise +``` + +The cause is documented in the product itself (`crates/maxplayer-core/src/home.rs:2198-2205`): + +> THE CREDENTIAL DOES NOT CROSS INTO THE CONTAINER. A container inherits no home directory and no +> macOS Keychain, so a `claude /login` credential is unreachable inside the container. `doctor` still +> passes — it runs no agent turn — but the pre-advertise probe runs INSIDE the container and FAILS, +> so the seat never advertises rather than advertising and failing every job. + +So a host-side interactive login is not a provisioning path for a Docker seat. The daemon must hold +the credential in **its own process environment**. + +## The required provider and credential + +This seat runs `maxplayer seller --agent claude`, so the provider is **Anthropic**. + +The credential is **`CLAUDE_CODE_OAUTH_TOKEN`**, produced by `claude setup-token`. + +`ANTHROPIC_API_KEY` is explicitly the wrong choice here, and this is the trap worth naming: +Claude Code prompts **once** to approve an API key found in the environment rather than using it +silently, and a daemon has nobody to give that approval. The probe therefore fails on a machine +where the variable is plainly set and looks correct (`docs/SELLER-QUICKSTART.md:490-495`). It is +also the usage-billed Console path rather than a subscription login (`:381-383`). + +The daemon's **built-in** forwarding list is (`crates/maxplayer-core/src/seller_exec.rs:301-311`, +`FORWARDED_AGENT_ENV`): `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, +`ANTHROPIC_BASE_URL`, `OPENAI_API_KEY`, `OPENAI_BASE_URL`. + +That is the built-in list, **not** the limit of what the daemon can forward: a configured +`[sandbox] forward_env` adds names on top of it. No extra `forward_env` is needed or recommended for +this seat — the built-in list already covers the credential named above. + +## How the value reaches the contained harness without being exposed + +`CLAUDE_CODE_OAUTH_TOKEN` is one of the four entries in `CONTAINED_CREDENTIALS` +(`seller_exec.rs:2761-2789`). The mechanism: + +1. The real value stays in the **daemon's** environment, on the host. +2. The container receives a **per-job placeholder** of matching shape (`sk-ant-oat01-` + 93 random + characters), never the real value. +3. At egress the proxy substitutes the real value for the placeholder, for the one upstream that + credential is bound to. + +On that upstream, precisely: the substitution target is the operator's **`ANTHROPIC_BASE_URL` when +it is set**, and `https://api.anthropic.com` only as the default when it is not +(`seller_exec.rs:2761-2789`, field `default_upstream`). So this is **not** unconditional destination +pinning — an operator who sets `ANTHROPIC_BASE_URL` changes where the real credential is sent, which +is precisely why the base-URL variable has to travel with the key instead of being dropped. + +Two bounds the source itself keeps separate, so I keep them separate too: verbatim header transport +is proven for `ANTHROPIC_API_KEY` (a single `x-api-key` header); for the OAuth token, it rides the +same value-substitution mechanism, but its verbatim travel and the client's compliance with the +redirect remain **contained-probe assumptions** until an actual probe artifact exists. + +The design is fail-closed: if a token were derived rather than sent verbatim, substitution would +miss and the job would fail to authenticate — a break, never a leak. A job that reads its own +environment sees a placeholder that is useless anywhere else and dies with the job. + +This is also why the containment story does not weaken to make auth work: the seat keeps its +dedicated `maxplayer-cashu-jobs` network and per-job egress proxy, and the credential is contained +*because* of them, not in spite of them. + +## Two assumptions that do NOT hold — checked, not assumed + +- **OpenClaw `SecretRef` is not a maxplayer feature.** Maxplayer reads credentials from its own + process environment (`FORWARDED_AGENT_ENV`) or from `[sandbox] file_credentials`; there is no + SecretRef resolution anywhere in the credential path. A SecretRef written into `config.toml` + would be forwarded as the literal string. +- **An OpenClaw opaque env sentinel cannot serve as the daemon credential.** Being precise about + why, because my earlier wording here was too strong: an environment string can of course be + inherited by a child process. What a sentinel does not provide is durable **resolution and egress + authorization** for a separately launched, long-lived daemon — OpenClaw's sentinels are + process-local and its egress-proxy authentication is run-scoped (installed + `docs/gateway/secrets.md`), while this seat must hold a usable credential for the life of the seat + and every job it launches. So do not store a sentinel or a SecretRef literal as the seller + credential: it would be forwarded intact and resolve to nothing. + +- **`[sandbox] file_credentials` cannot express a `claude /login` credential.** It reads exactly one + **top-level** JSON field (`crates/maxplayer-core/src/home.rs:761-779`); the OAuth file that + `/login` leaves behind nests the token under a parent object. That option is built for clients + like `cursor-agent` that need an argv endpoint flag, and it is not the path for claude. + +## Protected-store state — dated and attributed, not certified + +As relayed to me with this order on **2026-09-09**, the OpenClaw protected store held six entries: +five Discord bot tokens and one gateway token, with no model credential. That is a **second-hand, +dated statement**, not something I verified: I did not inspect store metadata, and I therefore +cannot certify what the store contains now. + +It does not matter much either way, because this seat could not consume a model credential from +that store regardless, for the two reasons above. The supported route below is external to it. +Nothing in the store was read to write this document. + +## The concrete human setup step + +This is a human decision and a human action. It requires a Claude subscription account, and whoever +runs it holds the token; I neither see nor handle the value. + +1. In your own terminal, as the account you want this seat to bill against: + + ```bash + claude setup-token + ``` + + This prints a long-lived, **model-only** token. It is not a full account credential. + +2. Put it in a mode-`0600` environment file owned by the user that runs the daemon, as + `CLAUDE_CODE_OAUTH_TOKEN=...` — one line, nothing else. Do not paste it into chat, a commit, + `config.toml`, shell history, or a ticket. Suggested location for this seat: + `~/forge/v2/seats/cashu-cdk-seller/daemon.env` (`chmod 0600`). + +3. Start the seller with that file sourced into the daemon's environment only — for example + `set -a; . ~/forge/v2/seats/cashu-cdk-seller/daemon.env; set +a` in the shell that launches it, + or `EnvironmentFile=` on a systemd unit. + + An **already-running** daemon cannot pick this up: a later export does not reach a process that + has already started, so this must be the environment of a **new** launch. + + For the image and the exact start procedure, follow **`RUNBOOK-ADDENDUM.md`**, which is the + authoritative operational handoff. Do **not** use `RUNBOOK.md` for this — it is retained as the + historical first-round record and its image tag, image id and gate figures are stale. In + particular, complete the addendum's §6 selected-field image change (and leave the dedicated + network, closed admissions and `accepted_mints` exactly as they are) **before** starting the + seat, so the first advertise happens on the corrected image. + +4. Confirm the seat advertises: the boot log should show the pre-advertise probe producing an + artifact instead of `Authentication required`, and the seat then publishing kind-0 and + kind-30340. + +Step 4's output is the evidence that is missing today. Until a human completes steps 1–3, no +discovery evidence can exist, and none is claimed. + +## What is still unproven after that + +Provisioning auth proves auth. It does not by itself prove the contained harness end-to-end. Once +the seat advertises, the remaining evidence to capture is: a probe artifact from inside the +container, kind-0 and kind-30340 discovery events, and one **test-only** job run end to end. No paid +job, no funding, no open admissions. diff --git a/reports/cashu-cdk-seller/EVIDENCE.md b/reports/cashu-cdk-seller/EVIDENCE.md new file mode 100644 index 000000000..b412e94f5 --- /dev/null +++ b/reports/cashu-cdk-seller/EVIDENCE.md @@ -0,0 +1,103 @@ +# Cashu/CDK specialist seat — evidence + +Build completion and live readiness are reported **separately**, because they are not the same +thing and only one of them is finished. + +## A. Build — COMPLETE, verified + +### Acceptance gate: PASS, 22 assertions across 8 legs, exit 0 + +Full transcript: `acceptance-run-20260909.txt`. Counts, not adjectives: + +| leg | asserted | +|---|---| +| 1 mint info (NUT-06) | version `cdk-mintd/0.17.2`; 3 NUT-04 methods advertised | +| 2 mint quote + issue | 64 sats minted; wallet balance 64 | +| 3 send + receive | token 954 chars; sender 64 → 43 (sent 21, fee 0); receiver credited 21, balance 21 | +| 4 double spend | second receive of the same token rejected: `Token Already Spent`; double-spender balance 0 | +| 5 melt | melt amount 5; balance 21 → 15 (melt 5, fee 1) | +| 6 failed payment | injection payload round-tripped through the BOLT11 description; wallet errored `Payment failed`; balance 15 → 15 — nothing lost | +| 7 restart recovery | `test-mint restart`; sender 43 → 43, receiver 15 → 15; a spend AFTER the restart succeeded (1 sat received); the pre-restart spent token still rejected — no duplicate credit | +| 8 loopback only | `/proc/net/tcp` `local_address` for the 8085 listener = `0100007F`; TCP connect to the container's own bridge address `172.17.0.2:8085` REFUSED | + +No leg is a no-op and none was skipped. Every failure mode named in the order was simulated; if one +could not have been, the harness `bail!`s rather than printing a pass (see the `container_ip()` +branch). + +### Four defects found by running rather than reading + +1. **`cdk-mintd` needs `protoc` at build time even with `--no-default-features`.** It depends on + `cdk-signatory` unconditionally and that crate's `build.rs:14` compiles its proto regardless of + the `grpc` feature. First build failed exactly there. +2. **`[ln]` min/max mint+melt are REQUIRED**, though `cdk-mintd`'s own `example.config.toml` + comments them out. `struct Ln` (`src/config.rs:170-179`) has no `#[serde(default)]` for them; + omitting one kills the whole file with `data did not match any variant of untagged enum + LnOneOrMany for key 'ln'`, which names neither the field nor the cause. +3. **The internal roster note's mint config is wrong at 0.17.2.** It describes + `[payment_backend] backend = "fakewallet"`; the real schema is `[ln] ln_backend = "fakewallet"` + plus a `[fake_wallet]` table. Its loopback `127.0.0.1:8085` claim was correct. +4. **`pay_err: true` alone does NOT simulate a failed payment.** `cdk-fake-wallet` + (`src/lib.rs:706-714`) inserts `check_payment_state` into its payment-states map *before* it + honours `pay_err`, so with the struct's `Paid` default the melt finalises `state=Paid, + amount=5, fee_paid=0` for an invoice the backend refused. `check_payment_state: Unpaid` is + required too. A harness asserting only "the call returned" would have reported false coverage + here. + +### Containment + +- Mint binds loopback and `test-mint` refuses any other bind (enforced in the script, not merely + documented). +- The mint binary is built without cln/lnd/lnbits/bdk/ldk-node compiled in, so it cannot be pointed + at a real payment backend by any configuration. +- Seat job containment: dedicated docker network `maxplayer-cashu-jobs`; doctor reports the policy + renders **23 rules**, installed in each job's own netns before the job starts. +- The test mint appears in **no** `accepted_mints`. The seat's accepted set is the shipped default, + unchanged. +- No secret value appears in any artifact, log, command line or report. The mint seed is generated + per reset into a `0600` file and passed by `--seed-file`. + +## B. Live readiness — PARTIAL, and blocked on a human decision + +`doctor-20260909.txt`: **17 checks, exit 0.** PASS on nix, seller key, relay reachability +(NIP-42 authenticated), mint reachability, sandbox launcher, sandbox image, sandbox egress +(23 rules), engine floor, containment probe, home permissions (0700). + +The seat starts, authenticates to the relay, and publishes its relay-git announce +(`seller-boot-20260909.log`). It then **refuses to advertise**: + +``` +04:53:05Z pre-advertise probe FAILED claude: this is an AUTHENTICATION failure + (ACP request 3 failed: {"code":-32000,"message":"Authentication required"}) +prove-before-advertise: none of 1 configured harness(es) produced a probe artifact; + refusing to advertise +``` + +**This is correct fail-closed behaviour, not a defect.** The node proves a harness can actually run +before it advertises a capability. The daemon's environment holds none of the four contained +credentials (`ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, +`OPENAI_API_KEY` — `seller_exec.rs:2763-2786`), so the #647 proxy has no real value to substitute +for the per-job placeholder and the agent inside the container cannot authenticate. + +The file-based route does **not** cover this harness at 0.5.8: `[sandbox] file_credentials` reads a +single **top-level** JSON key (`read_file_credential`, `seller_exec.rs:2904-2920`, a plain +`.get(&cred.field)` with no JSON-pointer support), while claude-code's OAuth file nests its token. +Verified in source, not assumed. + +**Not attempted by design.** Provisioning a model credential for a stranger-facing seat is a human +decision and a protected-credential operation. No credential was requested, handled, echoed or +stored by this lane. + +### Therefore + +- Discovery evidence — kind-0 profile and the kind-30340 capability heartbeat — **cannot be + produced yet**, because the node withholds them until a harness proves out. Saying otherwise + would be claiming a marketplace surface that does not exist. +- **No marketplace readiness is claimed.** A process starting is not readiness, and this process is + explicitly declining to advertise. + +### The one remaining step, for whoever holds the decision + +Provision one contained model credential into the seller daemon's environment through supported +protected setup, then restart the seat with the same command in the runbook. The pre-advertise +probe will then either pass — at which point kind-0 and kind-30340 appear and discovery evidence can +be captured — or fail for a different, reportable reason. diff --git a/reports/cashu-cdk-seller/FINDINGS.md b/reports/cashu-cdk-seller/FINDINGS.md new file mode 100644 index 000000000..75202c952 --- /dev/null +++ b/reports/cashu-cdk-seller/FINDINGS.md @@ -0,0 +1,74 @@ +# cashu-cdk-seller — measured findings (worker lane) + +Worktree: `~/forge/v2/wt/w-cashu-cdk-seller`, branch `w/cashu-cdk-seller` +Base: `d7b94db2dbb7aeeefdcbb087edd0c90df56a8bdb` (upstream `https://github.com/MakePrisms/maxplayerai.git` main, "release: cut v0.5.8 (#984)", 2026-09-08 19:23:56 -0700) +Workspace version: `0.5.8` (root `Cargo.toml`) + +## Host / runtime (measured 2026-09-08 ~21:11 PDT) + +- Docker daemon was DOWN. Existing colima profile `default` (aarch64, 4 CPU, 4 GiB, 40 GiB, docker + runtime) was Stopped; started it. `docker info` now: server `29.5.2`, Ubuntu 24.04.4 LTS, aarch64, + 4 cpu, 4094005248 bytes. No infrastructure acquired; reversible with `colima stop`. +- Pre-existing images in that VM: buzz, minio, minio/mc, postgres:17-alpine, redis:7-alpine. + +## CDK pins at this head (first-hand, not from the roster note) + +`crates/maxplayer-core/Cargo.toml`: `cashu = "=0.17.2"` (:59), `cdk = "=0.17.2"` default-features +off + `wallet` (:80), `cdk-sqlite = "=0.17.2"` (:81, and :115 dev). `crates/maxplayer/Cargo.toml`: +same three at `=0.17.2` (:105-107). Gated behind the `wallet` feature (:41). + +Cargo registry cache already holds 0.17.2 sources for: cashu, cdk, cdk-axum, cdk-cli, cdk-common, +cdk-fake-wallet, cdk-http-client, **cdk-mintd**, cdk-prometheus, cdk-signatory, cdk-sql-common, +cdk-sqlite. + +## CORRECTION to the roster note's mintd config + +The roster note (addendum-1-damian-roster.md §1) states `[payment_backend] backend = "fakewallet"` +and `CDK_MINTD_LN_BACKEND=fakewallet`. That is **not** the schema at the pinned 0.17.2. Read from +`~/.cargo/registry/src/index.crates.io-*/cdk-mintd-0.17.2/`: + +- `src/config.rs:1002` `struct Settings` fields: `info`, `mint_info`, `ln: Vec` + (`deserialize_ln`), `onchain`, `limits`, ... `fake_wallet: Option` (:1019-1020), + `database`, `mint_management_rpc`. +- `example.config.toml:116-119`: section is `[ln]` with `ln_backend = "fakewallet"`. Repeat + `[[ln]]` for one backend per unit; duplicate (unit, method) pairs are rejected at startup. + Comment at :118 — "NOTE: fakewallet is isolated testing mode and cannot be mixed with real + payment backends." +- `example.config.toml:283` `[fake_wallet]` with `fee_percent`, `reserve_fee_min`, + `custom_payment_methods`, `min_delay_time`, `max_delay_time`, optional + `[[fake_wallet.keyset_rotations]]` (unit / version v1|v2 / input_fee_ppk / expired) for + inactive/expired test keysets. +- `[onchain] onchain_backend = "fakewallet"` also exists (:126). +- Default listen is `[info] listen_host = "127.0.0.1"`, `listen_port = 8085` — loopback by default, + which matches the containment requirement. +- `Cargo.toml [features] default` DOES include `fakewallet` (plus management-rpc, cln, lnd, lnbits, + grpc-processor, sqlite, info-page, bdk). `fakewallet = ["dep:cdk-fake-wallet"]`. So a + minimal-feature build for the test mint is `--no-default-features --features fakewallet,sqlite` + (to be verified by building). + +The roster note's default listen `127.0.0.1:8085` claim is CONFIRMED. + +## Sandbox image + +- Default image is `ghcr.io/makeprisms/maxplayer-sandbox:v` — + `crates/maxplayer-core/src/seller_exec.rs:236`. +- **v0.5.8 is NOT published.** ghcr tags list (anonymous pull token, 2026-09-08): v0.5.0-rc1..rc5, + v0.5.0, latest, v0.5.1-rc1, v0.5.2, v0.5.3, v0.5.4, v0.5.5, v0.5.6, **v0.5.7** — no v0.5.8. + The release was cut tonight and CI has not pushed the image yet. +- `docker/maxplayer-sandbox/Dockerfile` builds from the repo root, multi-stage: nixos/nix:2.31.2 + store copy + a cargo builder stage for the `maxplayer` binary (`acp,wallet`), with cache mounts. + +## accepted_mints — the line to hold + +`crates/maxplayer-core/src/payment_wallet.rs` enforces the seller's advertised mint set twice: +`:207,:231` realized mint must be in `accepted_mints` else `wrong_mint`; `:253-263` the NUT-18 +payload mint must be in `accepted_mints` AND equal the declared mint. The test mint must never +appear there. + +## Containment + +`docs/SANDBOXING.md` §1: loopback is never denied inside the job network namespace (docker's +embedded resolver at 127.0.0.11 lives there); denies are destination-scoped on RFC1918, CGNAT +100.64/10, link-local 169.254/16, 198.18/15, multicast, reserved, and the host itself, across both +address families, in `INPUT` and `DOCKER-USER`. Policy source: `crates/maxplayer-core/src/sandbox_net.rs`. +So a mint bound to 127.0.0.1 inside the job container is reachable by the job and by nothing else. diff --git a/reports/cashu-cdk-seller/RUNBOOK-ADDENDUM.md b/reports/cashu-cdk-seller/RUNBOOK-ADDENDUM.md new file mode 100644 index 000000000..fa9d3c75f --- /dev/null +++ b/reports/cashu-cdk-seller/RUNBOOK-ADDENDUM.md @@ -0,0 +1,189 @@ +# AUTHORITATIVE operational handoff — cashu-cdk-seller + +**This file supersedes `RUNBOOK.md` for anything to do with images, the acceptance command, and +starting the seat.** `RUNBOOK.md` is kept unmodified as the historical record of the first round; its +image tag (`maxplayer-cashu-sandbox:v0.5.8-local`), its image id (`eb91a9…`) and its "22/8" gate +figure are **stale and must not be used**. Where the two disagree, this file wins. + +Written 2026-09-09 by the worker seat. Everything below was measured on this host, not recalled. + +## 1. The image to use + +| | | +|---|---| +| Tag | `maxplayer-cashu-sandbox:v0.5.9-fix2` | +| **Immutable id** | `sha256:7fe93019c0fedc08242b3ff9f80d95fafa2f7c9cf3a7d1af6257bddff4ebc4c0` | +| Built from source commit | `a3b6a700f97067d733e04b0b24a5262b2dd1face` | +| Built | 2026-09-09T17:47:27-07:00 | +| Size | 999,306,918 bytes | +| CDK | 0.17.2, upstream commit `6132607495ae0741e412a63f2acc34e4ccddfc55` (tag v0.17.2) | +| NUTs corpus | commit `49a909ce4d0739824b3859d4b3da21e6c1abdaeb` | +| Test state root | `/var/lib/cashu-test-state` (container-local, outside `/work`) | + +**Always run it by id, never by tag.** A tag is mutable and a rebuild silently moves it; the id is +the artifact. Every command in this file uses the id for that reason. + +### Provenance — what the id does and does not prove + +Stated plainly, because this is the part that is easy to overclaim: + +- The image was built **locally on this host** (rocky's Mac Studio) inside the colima VM with + `docker buildx`, from the worktree at commit `a3b6a700`. It is **not published to any registry** + and has no registry digest. +- `ai.maxplayer.cashu.source-commit=a3b6a700…` is a label **I set at build time**. It is a + self-asserted claim, not a cryptographic attestation: a label proves what the builder wrote, not + that the layers correspond to that source. There is no signature, SBOM, or provenance attestation + on this image. +- What *is* independently pinned is inside the Dockerfile: every `FROM` is digest-pinned, the base + is `maxplayer-sandbox@sha256:4b644531…`, `cdk-mintd` and the three helper crates install + `--locked` against committed `Cargo.lock` files, and the example builds `--offline --locked`. + That gives reproducible **dependency graphs**, not bit-for-bit image equality — apt packages float + by design and their exact versions are recorded in `/opt/rust/TOOLCHAIN.txt` inside the image. +- To rebuild and get a comparable artifact, check out `a3b6a700` and run the build in §5. Expect a + different image id (timestamps and apt state differ); compare `/opt/rust/TOOLCHAIN.txt` and the + labels, not the id. + +## 2. Evidence status for this image — read before quoting numbers + +- **Accepted, retained, and NOT re-run:** the 39-assertion / 10-leg acceptance run and the F1/F2/F3 + runtime evidence, captured on the previous image + (`sha256:4fa79465132a6cd655245320982d6995fb0b97497f0b0f043cb18eec13cd5966`) and recorded in + `delta-*-20260909.txt`. The ordering seat directed that this run stand and not be repeated. +- **This image adds** corrected F4/F5 predicates and the corrected baked knowledge topics. Its + targeted evidence is `delta2-predicate-regression-20260909.txt` (10 oracle requirements, 3 + fixtures) and `delta2-offline-compile-check-20260909.txt`. +- **Therefore: do not quote an assertion count for the corrected gate.** The corrected + `mint-acceptance` has more assertions than 39, and I have deliberately not run it to obtain a + number, because the order was to produce targeted predicate evidence rather than another green + transcript. Anyone who wants a corrected full-gate figure must run §4 and read the output. + +## 3. Non-root, bind-mounted immutable replay recipe + +This is the exact shape used to produce the retained runtime evidence: a **host directory bound at +`/work`** (so delivery isolation is real, not simulated) and the container running as a **non-root +host uid**, so anything written to the delivered workdir is owned by the seller and not by root. + +```bash +IMG=sha256:7fe93019c0fedc08242b3ff9f80d95fafa2f7c9cf3a7d1af6257bddff4ebc4c0 +WORK=$(mktemp -d) # the delivered workdir; must be EMPTY at start + +docker run --rm \ + --user "$(id -u):$(id -g)" \ + --network none \ + -v "$WORK:/work" -w /work \ + "$IMG" \ + bash -lc 'set -e; test-mint start >/dev/null; mint-acceptance; test-mint isolation' +``` + +Notes that matter, each one learned the hard way: + +- `--user "$(id -u):$(id -g)"` — on this host that is `502:20`. The image has no passwd entry for + that uid, which is why everything the job must read is world-readable and `CARGO_HOME` is + world-writable. Running as root would invalidate the delivery-ownership property. +- `--network none` is correct for the acceptance gate: the mint is loopback-only and the toolchain + is offline (`CARGO_NET_OFFLINE=true`), so the gate needs no route out at all. Proving it passes + with no network is stronger than proving it passes with one. +- `bash -lc`, not `bash -c`: a **login** shell is what a job harness typically gives you, and it is + the case that broke once — Debian's `/etc/profile` assigns `PATH` unconditionally, so the Rust + toolchain vanished in an image that contained it. `/etc/profile.d/10-rust-toolchain.sh` now + repairs that, and using `-l` here keeps the repair under test. +- `$WORK` must start empty. `test-mint isolation` asserts no mint state appears in `/work`; seeding + the directory first makes that assertion meaningless. +- State lives at `/var/lib/cashu-test-state` **inside the container** and dies with it. Nothing to + clean up on the host beyond `$WORK`. + +For the F1 offline-build proof and the F4/F5 predicate regression specifically: + +```bash +# F1: toolchain + CDK cache, offline compile, then run the example against the mint +docker run --rm --user "$(id -u):$(id -g)" --network none "$IMG" \ + bash -lc 'cashu-toolchain-check --run' + +# F4/F5: the predicate regression. Source is mounted because the binary is a test artifact, +# not part of the shipped image surface. +docker run --rm --user "$(id -u):$(id -g)" --network none \ + -v "$PWD/docker/maxplayer-cashu-sandbox/acceptance:/src:ro" "$IMG" \ + bash -lc 'set -e; cp -a /src /tmp/acc && cd /tmp/acc + CARGO_TARGET_DIR=/tmp/t cargo build --offline --locked --bin predicate-regression + test-mint start >/dev/null + /tmp/t/debug/predicate-regression' +``` + +The regression is the one that can genuinely fail: it reproduces a wallet with **42 of 43 sats +stranded in Reserved**, requires the shipped predicates to accept it, and requires the corrected +predicates to reject it. If a future edit weakens a predicate, this exits non-zero. + +## 4. Corrected acceptance command + +Replaces `RUNBOOK.md:17-18`. Prints the corrected gate's own count; do not assume 39. + +```bash +IMG=sha256:7fe93019c0fedc08242b3ff9f80d95fafa2f7c9cf3a7d1af6257bddff4ebc4c0 +WORK=$(mktemp -d) +docker run --rm --user "$(id -u):$(id -g)" --network none -v "$WORK:/work" -w /work "$IMG" \ + bash -lc 'test-mint start >/dev/null && mint-acceptance' +``` + +## 5. Rebuilding + +Host `docker` CLI here has no buildx, so the build runs inside the colima VM, which sees the same +path over virtiofs: + +```bash +cd ~/forge/v2/wt/w-cashu-cdk-seller +HEAD=$(git rev-parse HEAD) +colima ssh -- bash -lc "cd $PWD && docker buildx build \ + --label ai.maxplayer.cashu.source-commit=$HEAD \ + -f docker/maxplayer-cashu-sandbox/Dockerfile -t maxplayer-cashu-sandbox:local ." +colima ssh -- bash -lc 'docker image inspect maxplayer-cashu-sandbox:local --format "{{.Id}}"' +``` + +The build itself proves the offline CDK compile (it fails if the runtime toolchain, the crate cache +or the C linker is missing), so a successful build is already a meaningful check. + +## 6. VPS / seat image setup — HUMAN ONLY, selected field only + +**I have not performed this and it is not authorized to me.** Whoever does it changes **exactly one +field** and nothing else. + +In the seat's `config.toml` (`~/forge/v2/seats/cashu-cdk-seller/config.toml`), set only the +`[sandbox]` image field to the id from §1: + +```toml +[sandbox] +image = "sha256:7fe93019c0fedc08242b3ff9f80d95fafa2f7c9cf3a7d1af6257bddff4ebc4c0" +``` + +Preserve, do not touch: + +- **`network` and `proxy_port_range`** — the dedicated `maxplayer-cashu-jobs` network and its + per-job egress proxy are what contain a job and what contain the model credential. Removing or + widening either breaks containment. +- **Admissions.** The seat runs with neither `--claim-open-pool` nor `--accept-open-targeted` and no + `accept_offers_only_from`, so it claims nothing and owes nothing. Leave it that way; opening + admissions is a separate authorization. +- **`accepted_mints`.** Shipped default (`mint.minibits.cash`) and unchanged. The sandbox test mint + must **never** appear there — it issues worthless test ecash and is enforced in two places in + `crates/maxplayer-core/src/payment_wallet.rs`. +- **Every other key in the file.** Edit the one field; do not regenerate or overwrite the config. + +Verify afterwards, before starting the seat: + +```bash +grep -n 'image\|network\|proxy_port_range\|accepted_mints' ~/forge/v2/seats/cashu-cdk-seller/config.toml +./target/release/maxplayer doctor # expect the same 17 checks, exit 0 +colima ssh -- bash -lc 'docker image inspect sha256:7fe93019c0fedc08242b3ff9f80d95fafa2f7c9cf3a7d1af6257bddff4ebc4c0 --format "{{.Id}}"' +``` + +The last command failing means the id is not present on the host that will run the jobs — build or +load it there first. A seat pointed at an absent image fails at job launch, not at `doctor`. + +## 7. Auth + +Credential provisioning is a separate, human step: see **`AUTH-PROVISIONING.md`**, which points back +to this file for the image and the start procedure. Do §6 **before** the human provisions and starts +the seat, so the first advertise happens on the corrected image. + +Authentication succeeding is not deployment acceptance. After the seat starts, the things still to +capture are a contained probe artifact, actual kind-0 / kind-30340 discovery evidence, and a +separately authorized **test-only** end-to-end job. None of those exist today and none is claimed. diff --git a/reports/cashu-cdk-seller/RUNBOOK.md b/reports/cashu-cdk-seller/RUNBOOK.md new file mode 100644 index 000000000..ed730765c --- /dev/null +++ b/reports/cashu-cdk-seller/RUNBOOK.md @@ -0,0 +1,121 @@ +# Cashu/CDK specialist seat — runbook (HISTORICAL — first round, 2026-09-08/09) + +> ⚠️ **Superseded for operations. Do not follow this file to deploy or start the seat.** +> Use **`RUNBOOK-ADDENDUM.md`**, which is the authoritative operational handoff. +> +> This file is retained unchanged below as the historical first-round record, because its evidence +> is real and was accepted. But three things in it are now **stale and wrong to act on**: the image +> tag `maxplayer-cashu-sandbox:v0.5.8-local`, the image id `eb91a9…`, and the "22/8" acceptance +> figure. The addendum carries the current immutable image id, its provenance bounds, the non-root +> bind-mounted replay recipe, and the human-only selected-field seat setup. +> +> Nothing below has been edited; only this banner was added. + +Everything below was run first-hand on rocky's Mac Studio on 2026-09-08/09 (PDT). Nothing here is +relayed from a prior note. + +## What exists + +| thing | value | +|---|---| +| worktree | `~/forge/v2/wt/w-cashu-cdk-seller`, branch `w/cashu-cdk-seller` | +| base commit | `d7b94db2dbb7aeeefdcbb087edd0c90df56a8bdb` (upstream/main, v0.5.8) | +| upstream resolved by URL | `https://github.com/MakePrisms/maxplayerai.git` (remote name `upstream`; `origin` is the fork) | +| seat home | `~/forge/v2/seats/cashu-cdk-seller` (OUTSIDE the worktree, survives worktree removal) | +| seller pubkey | `ece4939aa2ac61c661891ef81295d169be1762e4b74994e4929d57882343a86b` | +| npub | `npub1anjf8x4z43suvcvfrmup99w3dxlpwchykayefeyjn4tcsg6r4p4sreewtj` | +| host binary | `/target/release/maxplayer` — `maxplayer 0.5.8 (c449ba293c26ba1b625c9a11953022c7a801693f)` | +| base image | `maxplayer-sandbox:v0.5.8-local` `sha256:4b644531ffe3fdc0c4414aa8d6f9027c58a3f18124bb7abe61bb622973731eea` | +| specialist image | `maxplayer-cashu-sandbox:v0.5.8-local` `sha256:eb91a9bfa200226409f023cf0b15606e32463554bcf2841966d2140828a79cd3` | +| job network | docker user-defined network `maxplayer-cashu-jobs` | +| deployment location | this Mac Studio, in the colima VM profile `default` (docker 29.5.2, Ubuntu 24.04.4, aarch64, 4 CPU, 4 GiB) | + +## Pinned versions + +- `cashu` / `cdk` / `cdk-sqlite` — `=0.17.2` (the workspace's own pins; the mint is built at the + same version so a protocol mismatch cannot be mistaken for a product bug). +- `cdk-mintd` `0.17.2`, built `--no-default-features --features fakewallet,sqlite`. +- corpus: `cashubtc/nuts` @ `49a909ce4d0739824b3859d4b3da21e6c1abdaeb` (2026-08-23); + `cashubtc/cdk` @ `6132607495ae0741e412a63f2acc34e4ccddfc55` (tag `v0.17.2`). +- base image ancestry: `nixos/nix:2.31.2`, `rust:1-bookworm`, `node:22-bookworm-slim`, + `debian:bookworm-slim`. + +## THE ACCEPTANCE COMMAND (run this one line) + +```sh +docker run --rm maxplayer-cashu-sandbox:v0.5.8-local bash -c 'test-mint start >/dev/null && mint-acceptance' +``` + +No real funds, no external payment, no host state touched, container is `--rm`. Exit 0 = pass; it +prints an assertion count and every amount it asserted. Last run: **PASS — 22 assertions across 8 +legs** (`acceptance-run-20260909.txt`). + +## Rebuilding the images + +The host docker CLI has **no buildx plugin**, so `docker build` cannot honour the repo's +`RUN --mount=type=cache` lines. The colima VM has buildx v0.34.1 and mounts `/Users/forge` over +virtiofs at the same path, so build inside the VM: + +```sh +colima start # if not running +colima ssh -- bash -lc 'cd /Users/forge/forge/v2/wt/w-cashu-cdk-seller && \ + docker buildx build -f docker/maxplayer-sandbox/Dockerfile \ + --build-arg MAXPLAYER_BUILD_COMMIT=$(git rev-parse HEAD) \ + -t maxplayer-sandbox:v0.5.8-local .' +colima ssh -- bash -lc 'cd /Users/forge/forge/v2/wt/w-cashu-cdk-seller && \ + docker buildx build -f docker/maxplayer-cashu-sandbox/Dockerfile \ + --build-arg BASE_IMAGE=maxplayer-sandbox:v0.5.8-local \ + -t maxplayer-cashu-sandbox:v0.5.8-local .' +``` + +The base image is built locally because **`ghcr.io/makeprisms/maxplayer-sandbox:v0.5.8` does not +exist** — the anonymous tag list ends at `v0.5.7`; 0.5.8 was cut at 19:23 PDT on 2026-09-08 and CI +has not pushed the image. When CI publishes it, `BASE_IMAGE` can point at the registry tag instead. + +## The test mint + +Inside any container from the specialist image: + +```sh +test-mint start | status | info | restart | reset | logs [n] | url +``` + +- Binds `127.0.0.1:8085` and refuses any non-loopback bind. +- State in `$TEST_MINT_WORK_DIR` (default `/work/.test-mint`), dies with the container. +- `restart` keeps the database (restart-recovery testing); `reset` is the clean slate. +- Seed is a throwaway BIP-39 mnemonic generated per reset into a `0600` file, passed by + `--seed-file`. Never printed, never an argument, never baked into the image. + +**This mint is never added to any seller's `accepted_mints`.** The seat's accepted-mint set is +unchanged from the shipped default (`mint.minibits.cash`); doctor confirms "all 1 accepted mint(s) +reachable". + +## Running the seat + +```sh +cd ~/forge/v2/wt/w-cashu-cdk-seller +./target/release/maxplayer doctor --home ~/forge/v2/seats/cashu-cdk-seller +./target/release/maxplayer seller --agent claude --rate-sats 500 \ + --name "cashu-cdk specialist" --home ~/forge/v2/seats/cashu-cdk-seller +``` + +Deliberately **without** `--claim-open-pool` and **without** `--accept-open-targeted`, and with no +`[seller] accept_offers_only_from`. In that configuration the node states at boot that it claims +nothing. No job obligation, no payment path, no real-money operation enabled. + +`config.toml` in the seat home carries only `[sandbox]` (mode/image/network) plus the `[seller]` +section the first boot wrote. No unrelated setting was replaced. + +## Stopping / cleaning up + +```sh +# seat +kill +# job network (only if nothing else uses it) +docker network rm maxplayer-cashu-jobs +# container runtime, back to how it was found +colima stop +``` + +The colima VM was found **Stopped** and was started for this job. Nothing was installed on the +host; no infrastructure was acquired. diff --git a/reports/cashu-cdk-seller/acceptance-run-20260909.txt b/reports/cashu-cdk-seller/acceptance-run-20260909.txt new file mode 100644 index 000000000..c3aa741f2 --- /dev/null +++ b/reports/cashu-cdk-seller/acceptance-run-20260909.txt @@ -0,0 +1,59 @@ +mint-acceptance — sandbox-local fakewallet mint at http://127.0.0.1:8085 +WORTHLESS TEST ECASH. No real funds, no external payment. + + +[1] mint info (NUT-06) + ok mint version: "cdk-mintd/0.17.2" + ok mint advertises NUT-04 mint support: 3 method(s) + +[2] mint quote + issue + ok proofs minted: 64 sats (wanted 64) + ok wallet balance after issue: 64 sats + +[3] send + receive across wallets + ok token is non-empty: 954 chars + ok sender debited exactly amount+fee: 43 sats (issued 64 - sent 21 - fee 0 = 43) + ok receiver credited: 21 sats (wanted 21) + ok receiver balance: 21 sats + +[4] double spend rejected + ok second receive of same token: rejected: Token Already Spent + ok double-spender balance stays zero: 0 sats + +[5] melt + ok melt amount: 5 sats + ok balance fell by amount+fee: 21 -> 15 (melt 5, fee 1) + +[6] failed payment + no balance lost + injected description: {"pay_invoice_state":"UNPAID","check_payment_state":"UNPAID","pay_err":true,"check_err":false} + invoice description read back: {"pay_invoice_state":"UNPAID","check_payment_state":"UNPAID","pay_err":true,"check_err":false} + ok failure payload survives the BOLT11 description round trip: parsed=true + ok mint did NOT pay the failing invoice: wallet call errored: Payment failed + reclaimed 0 sats of reserved proofs after the failure + ok no test ecash lost to the failed payment: 15 -> 15 sats + +[7] mint restart, no lost or duplicate balance + test-mint restart -> test-mint: stopped (pid 14) +test-mint: up at http://127.0.0.1:8085/ (pid 73, work-dir /work/.test-mint) + ok sender balance unchanged by restart: 43 -> 43 sats + ok receiver balance unchanged by restart: 15 -> 15 sats + ok spend works after restart: 1 sat received + ok pre-restart spent token still rejected: rejected: Token Already Spent + +[8] loopback-only reachability + ok a listener exists on 8085: ["0100007F"] + ok every 8085 listener is bound to loopback: /proc/net/tcp local_address ["0100007F"] + ok mint NOT reachable on the container's own routable address: 172.17.0.2:8085 refused + +===================================================== +PASS — 22 assertions across 8 legs + 1. mint info (NUT-06) + 2. mint quote + issue + 3. send + receive across wallets + 4. double spend rejected + 5. melt + 6. failed payment + no balance lost + 7. mint restart, no lost or duplicate balance + 8. loopback-only reachability +mint: http://127.0.0.1:8085 (fakewallet, worthless test ecash) +===================================================== diff --git a/reports/cashu-cdk-seller/delta-f1-offline-compile-20260909.txt b/reports/cashu-cdk-seller/delta-f1-offline-compile-20260909.txt new file mode 100644 index 000000000..c0aec0dde --- /dev/null +++ b/reports/cashu-cdk-seller/delta-f1-offline-compile-20260909.txt @@ -0,0 +1,38 @@ +===F1-TOOLCHAIN-OFFLINE-COMPILE=== +== toolchain identity +rustc 1.98.1 (48a229cea 2026-09-01) +cargo 1.98.1 (797e8a9bc 2026-08-05) +CARGO_HOME=/opt/rust/cargo +CARGO_NET_OFFLINE=true +rustc 1.98.1 (48a229cea 2026-09-01) +cargo 1.98.1 (797e8a9bc 2026-08-05) +473M /out/rust/toolchain +112M /out/rust/cargo/registry +# runtime build environment, recorded at image build +rustc 1.98.1 (48a229cea 2026-09-01) +cargo 1.98.1 (797e8a9bc 2026-08-05) +gcc (Debian 12.2.0-14+deb12u1) 12.2.0 +GNU ld (GNU Binutils for Debian) 2.40 +gcc 4:12.2.0-3 +libc6-dev 2.36-9+deb12u14 +binutils 2.40-2 + +== dependency cache +cached .crate archives: 557 + present: cdk-0.17.2.crate + present: cashu-0.17.2.crate + present: cdk-sqlite-0.17.2.crate + +== offline build of /opt/cashu/examples/wallet-roundtrip +built: /var/lib/cashu-test-state/toolchain-check/target/debug/wallet-roundtrip +-rwxr-xr-x 2 root root 189977856 Sep 9 22:01 target/debug/wallet-roundtrip + +== running it against the test mint +mint: Some("cdk-mintd/0.17.2") +issued: 32 sats +alice after send: 24 sats +bob received: 8 sats +bob melted: state=Paid amount=4 fee=1 +bob final: 3 sats + +PASS — offline toolchain + CDK cache verified in-image diff --git a/reports/cashu-cdk-seller/delta-f2-knowledge-paths-20260909.txt b/reports/cashu-cdk-seller/delta-f2-knowledge-paths-20260909.txt new file mode 100644 index 000000000..725ab15fc --- /dev/null +++ b/reports/cashu-cdk-seller/delta-f2-knowledge-paths-20260909.txt @@ -0,0 +1,6 @@ +===F2-KNOWLEDGE=== +MEMORY.md +cashu-corpus.md +cdk-wallet-api.md +known-drift.md +test-mint.md diff --git a/reports/cashu-cdk-seller/delta-f3-state-isolation-20260909.txt b/reports/cashu-cdk-seller/delta-f3-state-isolation-20260909.txt new file mode 100644 index 000000000..5de7972fc --- /dev/null +++ b/reports/cashu-cdk-seller/delta-f3-state-isolation-20260909.txt @@ -0,0 +1,16 @@ +===F3-ISOLATION=== +state root: /var/lib/cashu-test-state +mint state dir: /var/lib/cashu-test-state/mint +delivered dir: /work +state inside delivered dir: no +state contents (names and sizes only, no values): + mintd.pid 5 bytes + mintd.log 8118 bytes + cdk-mintd.sqlite-wal 1998232 bytes + cdk-mintd.sqlite 4096 bytes + seed 76 bytes + cdk-mintd.sqlite-shm 32768 bytes + config.toml 2571 bytes +seed mode: 600 +delivered dir entries matching test-mint state: + none diff --git a/reports/cashu-cdk-seller/delta-f5-acceptance-20260909.txt b/reports/cashu-cdk-seller/delta-f5-acceptance-20260909.txt new file mode 100644 index 000000000..1db701c39 --- /dev/null +++ b/reports/cashu-cdk-seller/delta-f5-acceptance-20260909.txt @@ -0,0 +1,94 @@ +===F5-GATE=== +mint-acceptance — sandbox-local fakewallet mint at http://127.0.0.1:8085 +WORTHLESS TEST ECASH. No real funds, no external payment. + + +[1] mint info (NUT-06) + ok mint version is exactly cdk-mintd/0.17.2: cdk-mintd/0.17.2 + ok mint advertises NUT-04 mint support: 3 method(s) + +[2] mint quote + issue + ok proofs minted: 64 sats (wanted 64) + ok wallet balance after issue: 64 sats + +[3] send + receive across wallets + ok token is a cashu token: 954 chars + ok sender debited exactly amount+fee: 43 sats (issued 64 - sent 21 - fee 0) + ok receiver credited: 21 sats (wanted 21) + +[4] double spend rejected with the exact protocol error + ok second receive fails with Error::TokenAlreadySpent: TokenAlreadySpent + ok double-spender balance stays zero: 0 sats + +[5] melt + ok melt amount: 5 sats + ok melt finalised Paid: Paid + ok balance fell by exactly amount+fee: 21 -> 15 (melt 5, fee 1) + +[6] failed payment: exact error, terminal quote state, nothing lost + ok melt fails with Error::PaymentFailed: PaymentFailed + ok mint reports the failed quote as Unpaid: Unpaid + ok no test ecash lost to the failed payment: 15 -> 15 sats + ok nothing left stranded pending or reserved: pending 0, reserved 0 + +[7] recovery of an unresolved send across a wallet restart + ok recovery wallet funded: 32 sats + ok an unresolved send exists before restart: 1 pending send(s), 12 sats locked + ok the unresolved send survives the wallet restart: 1 pending send(s) after reopen + ok recovery reports a non-empty result: recovered 1, compensated 0, skipped 0, failed 0 + ok recovery failed nothing: failed 0 + ok revoke_send reclaims the stranded value: reclaimed 12 sats of 12 locked + ok spendable balance grows by exactly the reclaimed amount: 20 -> 32 (+12) + ok no unresolved sends remain: 0 left + ok check_all_pending_proofs returns the residual pending total: returned 0, wallet pending 0 + +[8] mint restart: full residual value still spendable, nothing duplicated + ok there is real residual value to test with: sender 43 + receiver 15 + recovery 32 = 90 + test-mint: stopped (pid 3443) | test-mint: up at http://127.0.0.1:8085/ (pid 3487, state /var/lib/cashu-test-state/mint) + ok sender: mint honoured the swept token after restart: swept 43 of 43 sats + ok receiver: mint honoured the swept token after restart: swept 15 of 15 sats + ok recovery: mint honoured the swept token after restart: swept 32 of 32 sats + ok swept value reconciles exactly with the pre-restart total: sink 90 = swept 90; swept + fees 0 = 90 pre-restart + ok sender fully drained, nothing stranded: spendable 0, pending 0 + ok receiver fully drained, nothing stranded: spendable 0, pending 0 + ok recovery fully drained, nothing stranded: spendable 0, pending 0 + ok pre-restart spent token still fails with Error::TokenAlreadySpent: TokenAlreadySpent + +[9] loopback-only reachability + ok a listener exists on 8085: ["0100007F"] + ok every 8085 listener is bound to loopback: /proc/net/tcp local_address ["0100007F"] + ok mint NOT reachable on the container's own routable address: 172.17.0.2:8085 refused + +[10] private test state is outside the delivered workdir + | state root: /var/lib/cashu-test-state + | mint state dir: /var/lib/cashu-test-state/mint + | delivered dir: /work + | state inside delivered dir: no + | state contents (names and sizes only, no values): + | mintd.pid 5 bytes + | mintd.log 8118 bytes + | cdk-mintd.sqlite-wal 1998232 bytes + | cdk-mintd.sqlite 4096 bytes + | seed 76 bytes + | cdk-mintd.sqlite-shm 32768 bytes + | config.toml 2571 bytes + | seed mode: 600 + | delivered dir entries matching test-mint state: + | none + ok test-mint isolation reports state outside the delivery dir: see the lines above + ok no mint state names appear in the delivered workdir: see the lines above + +===================================================== +PASS — 39 assertions across 10 legs + 1. mint info (NUT-06) + 2. mint quote + issue + 3. send + receive across wallets + 4. double spend rejected with the exact protocol error + 5. melt + 6. failed payment: exact error, terminal quote state, nothing lost + 7. recovery of an unresolved send across a wallet restart + 8. mint restart: full residual value still spendable, nothing duplicated + 9. loopback-only reachability + 10. private test state is outside the delivered workdir +mint: http://127.0.0.1:8085 (fakewallet, worthless test ecash) +===================================================== diff --git a/reports/cashu-cdk-seller/delta2-offline-compile-check-20260909.txt b/reports/cashu-cdk-seller/delta2-offline-compile-check-20260909.txt new file mode 100644 index 000000000..e9c6c3ae6 --- /dev/null +++ b/reports/cashu-cdk-seller/delta2-offline-compile-check-20260909.txt @@ -0,0 +1,40 @@ + Compiling scopeguard v1.2.0 + Compiling bs58 v0.5.1 + Compiling lock_api v0.4.14 + Compiling cbor-diag v0.1.12 + Compiling futures v0.3.34 + Compiling strum_macros v0.27.2 + Compiling serde_with v3.23.0 + Compiling regex v1.13.1 + Compiling reqwest v0.12.28 + Compiling simple_asn1 v0.6.4 + Compiling tokio-tungstenite v0.26.2 + Compiling ciborium v0.2.2 + Compiling unicode-normalization v0.1.25 + Compiling pem v3.0.6 + Compiling ahash v0.8.12 + Compiling strum v0.27.2 + Compiling web-time v1.1.0 + Compiling jsonwebtoken v9.3.1 + Compiling cdk-http-client v0.17.2 + Compiling parking_lot v0.12.5 + Compiling async-trait v0.1.92 + Compiling vcpkg v0.2.15 + Compiling libsqlite3-sys v0.28.0 + Compiling rustversion v1.0.23 + Compiling hashbrown v0.14.5 + Compiling cdk-sql-common v0.17.2 + Compiling hashlink v0.9.1 + Compiling fallible-iterator v0.3.0 + Compiling fallible-streaming-iterator v0.1.9 + Compiling tokio-stream v0.1.19 + Compiling arc-swap v1.9.2 + Compiling rusqlite v0.31.0 + Compiling cashu v0.17.2 + Compiling bitcoin-payment-instructions v0.7.1 + Compiling cdk-common v0.17.2 + Compiling cdk-fake-wallet v0.17.2 + Compiling cdk v0.17.2 + Compiling cdk-sqlite v0.17.2 + Compiling mint-acceptance v0.1.0 (/tmp/acc) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 34.62s diff --git a/reports/cashu-cdk-seller/delta2-predicate-regression-20260909.txt b/reports/cashu-cdk-seller/delta2-predicate-regression-20260909.txt new file mode 100644 index 000000000..77b520002 --- /dev/null +++ b/reports/cashu-cdk-seller/delta2-predicate-regression-20260909.txt @@ -0,0 +1,41 @@ + Compiling cdk-sqlite v0.17.2 + Compiling mint-acceptance v0.1.0 (/tmp/acc) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 33.47s +predicate-regression — do the corrected F4/F5 oracles actually discriminate? +mint http://127.0.0.1:8085 (fakewallet, worthless test ecash) + +[precondition] active keyset 014f0a4b3043b8287d216e5549a5fc1dce0a964fc75f8d2d82079586b184ea6484 charges input_fee_ppk 0 + +[A] HEALTHY sweep — the control. Both oracles must pass. + funded 40, locked 40, quoted fee 0, received 40; source pools spendable 0 pending 0 reserved 0 + ok A: old drain oracle + oracle says PASS, required PASS — nothing is actually stranded, so the weak oracle is right here too + ok A: corrected drain oracle + oracle says PASS, required PASS — all three pools empty + ok A: corrected quoted-fee conservation + oracle says PASS, required PASS — every sat arrived and the quoted fee was zero + +[B] STRANDED RESERVE — the counterexample. Old oracles must pass, corrected must fail. + funded 43, abandoned prepare_send of 42; pools now spendable 1 pending 0 reserved 42 + swept 1 with quoted fee 0; pools now spendable 0 pending 0 reserved 42 — 42 sats stranded out of 43 + ok B: old drain oracle accepts a wallet with value stranded in Reserved + oracle says PASS, required PASS — this is the defect, reproduced: it reports fully drained + ok B: corrected drain oracle REJECTS it + oracle says FAIL, required FAIL — reserved is non-zero, so the wallet is not drained + ok B: old derived-fee conservation accepts the shortfall as a fee + oracle says PASS, required PASS — this is the defect, reproduced: the missing value is relabelled a fee + ok B: corrected quoted-fee conservation REJECTS it + oracle says FAIL, required FAIL — CDK quoted no fee, so the shortfall has no explanation + +[C] RECOVERY oracles on a partial reclaim (arithmetic on the oracles, not a forced mint failure). + ok C: old recovery oracle accepts reclaiming 1 of 12 + oracle says PASS, required PASS — this is the defect: any positive reclaim passed + ok C: corrected recovery oracle REJECTS reclaiming 1 of 12 + oracle says FAIL, required FAIL — partial reclaim, so the funded total is not restored + ok C: corrected recovery oracle accepts a COMPLETE reclaim + oracle says PASS, required PASS — whole stranded amount back, funded total spendable, both other pools empty + +===================================================== +PASS — 10 oracle requirements met across 3 fixtures +The corrected predicates reject a case the shipped ones accepted. +===================================================== diff --git a/reports/cashu-cdk-seller/doctor-20260909.txt b/reports/cashu-cdk-seller/doctor-20260909.txt new file mode 100644 index 000000000..c8fdea6bd --- /dev/null +++ b/reports/cashu-cdk-seller/doctor-20260909.txt @@ -0,0 +1,21 @@ +maxplayer doctor — seller environment self-check (home=/Users/forge/forge/v2/seats/cashu-cdk-seller) +Docs for agents: https://www.maxplayer.ai/skill.md (or run `maxplayer skill`) +PASS nix — working nix on PATH (`nix --version` ran in this process's environment) +PASS credential helper — git-credential-nostr not found — OK, not required (seller signs NIP-98 in-process via libgit2) +PASS seller key — /Users/forge/forge/v2/seats/cashu-cdk-seller/key present +PASS relay reachability — wss://relay.maxplayer.ai: connected + NIP-42 authenticated +PASS relay token policy — delivery path: CONTAINER (the default for [sandbox] mode = "docker"; this seat does not set container_delivery); relay advertises scoped_token_max_lifetime_secs=21600 s — `fresh-after-agent` (in use) works, and `long-lived` is available up to 21600 s +PASS mint reachability — all 1 accepted mint(s) reachable +PASS agent preset — registry resolves: claude (preferred argv0=claude-agent-acp) — RESOLUTION ONLY: this proves the registry resolves the way boot does, NOT that any harness can deliver; executability is proven at the pre-advertise self-probe at boot, never here (a resolvable harness can still fail to run — #470/#252) +PASS telemetry — armed, no sink command configured (episodes.jsonl still captured) +PASS sandbox launcher — [sandbox] mode=docker, image 'maxplayer-cashu-sandbox:v0.5.8-local' — docker resolvable +PASS sandbox credential containment — every credential in the built-in table and in [sandbox] file_credentials is contained; no unrecognized forward_env var is set +PASS sandbox egress — network 'maxplayer-cashu-jobs' exists and the policy renders 23 rules; each job gets them installed in its own network namespace before it starts +PASS sandbox image — image 'maxplayer-cashu-sandbox:v0.5.8-local' present locally +PASS sandbox engine floor — Docker Engine 29.5.2 ≥ 25.0.0 — the default seccomp profile blocks io_uring_setup/io_uring_enter/io_uring_register +PASS sandbox containment — launcher confines: a file outside the workdir was refused, the workdir was writable (assumed deny-list for this platform: probed paths only — unlisted paths remain reachable) +WARN seat reachability — this seat can claim NOTHING: it names no buyers, does not accept targeted offers from unnamed buyers, and does not claim the open pool (fix: list the buyers you work with in `[seller] accept_offers_only_from`, or set `accept_open_targeted = true` to accept targeted offers from buyers you have not named, or set `claim_open_pool = true` to claim untargeted jobs from the open pool) +PASS home permissions — home and wallet are owner-only (0700) +PASS harness credential permissions — not group/world-writable: /Users/forge/.claude + +17 check(s), exit 0 diff --git a/reports/cashu-cdk-seller/seller-boot-20260909.log b/reports/cashu-cdk-seller/seller-boot-20260909.log new file mode 100644 index 000000000..f0030ffbd --- /dev/null +++ b/reports/cashu-cdk-seller/seller-boot-20260909.log @@ -0,0 +1,36 @@ +maxplayer seller home=/Users/forge/forge/v2/seats/cashu-cdk-seller key_present=true mint=https://mint.minibits.cash/Bitcoin relay=wss://relay.maxplayer.ai +agent preset=claude argv0=claude-agent-acp + claude also requires the `claude` CLI (npm i -g @anthropic-ai/claude-code), signed in — run `claude` once and complete `/login`, or set ANTHROPIC_API_KEY in the daemon's environment + the seat refuses to advertise until a probe turn actually succeeds, so an unauthenticated CLI fails at boot, not silently mid-job +git_remote defaulting to relay-git https://relay.maxplayer.ai/git/ece4939aa2ac61c661891ef81295d169be1762e4b74994e4929d57882343a86b/mece4939aa2ac61c6.git +wrote [seller] to /Users/forge/forge/v2/seats/cashu-cdk-seller/config.toml +Docs for agents: https://www.maxplayer.ai/skill.md (or run `maxplayer skill`) +maxplayer seller — startup readiness checks (auto-doctor; pass --skip-doctor to bypass) +PASS nix — working nix on PATH (`nix --version` ran in this process's environment) +PASS credential helper — git-credential-nostr not found — OK, not required (seller signs NIP-98 in-process via libgit2) +PASS seller key — /Users/forge/forge/v2/seats/cashu-cdk-seller/key present +PASS relay reachability — wss://relay.maxplayer.ai: connected + NIP-42 authenticated +PASS relay token policy — delivery path: CONTAINER (the default for [sandbox] mode = "docker"; this seat does not set container_delivery); relay advertises scoped_token_max_lifetime_secs=21600 s — `fresh-after-agent` (in use) works, and `long-lived` is available up to 21600 s +PASS mint reachability — all 1 accepted mint(s) reachable +PASS agent preset — registry resolves: claude (preferred argv0=claude-agent-acp) — RESOLUTION ONLY: this proves the registry resolves the way boot does, NOT that any harness can deliver; executability is proven at the pre-advertise self-probe at boot, never here (a resolvable harness can still fail to run — #470/#252) +PASS telemetry — armed, no sink command configured (episodes.jsonl still captured) +PASS sandbox launcher — [sandbox] mode=docker, image 'maxplayer-cashu-sandbox:v0.5.8-local' — docker resolvable +PASS sandbox credential containment — every credential in the built-in table and in [sandbox] file_credentials is contained; no unrecognized forward_env var is set +PASS sandbox egress — network 'maxplayer-cashu-jobs' exists and the policy renders 23 rules; each job gets them installed in its own network namespace before it starts +PASS sandbox image — image 'maxplayer-cashu-sandbox:v0.5.8-local' present locally +PASS sandbox engine floor — Docker Engine 29.5.2 ≥ 25.0.0 — the default seccomp profile blocks io_uring_setup/io_uring_enter/io_uring_register +PASS sandbox containment — launcher confines: a file outside the workdir was refused, the workdir was writable (assumed deny-list for this platform: probed paths only — unlisted paths remain reachable) +WARN seat reachability — this seat can claim NOTHING: it names no buyers, does not accept targeted offers from unnamed buyers, and does not claim the open pool (fix: list the buyers you work with in `[seller] accept_offers_only_from`, or set `accept_open_targeted = true` to accept targeted offers from buyers you have not named, or set `claim_open_pool = true` to claim untargeted jobs from the open pool) +PASS home permissions — home and wallet are owner-only (0700) +PASS harness credential permissions — not group/world-writable: /Users/forge/.claude +readiness OK — 17 check(s), 1 warning(s); starting seller +relay-git NIP-34 announce ok id=8ab00b30ae1c8635031abda272d351a5ceb75537bc3689738873d3a788a8f304 remote=https://relay.maxplayer.ai/git/ece4939aa2ac61c661891ef81295d169be1762e4b74994e4929d57882343a86b/mece4939aa2ac61c6.git +relay-git seed probe ok (info/refs reachable) +04:52:54Z seller node agent PASS claude binary resolves argv0=claude-agent-acp (auth not checked here — proven at the pre-advertise probe) +04:52:54Z seller node agents ready: ["claude"] (execution concurrency set by [seller] slots) +04:52:54Z seller node delivery path: CONTAINER — one container runs the agent and every git step. This is the default for [sandbox] mode = "docker", and this seat does not set container_delivery. Set container_delivery = false for the host path. +sandbox: capture_skipped=probe container=maxplayer-job-maxplayer-selfprobe-0-0-1788929574 +sandbox: job_cleanup=ok container=maxplayer-job-maxplayer-selfprobe-0-0-1788929574 +04:53:05Z seller node pre-advertise probe FAILED claude: this is an AUTHENTICATION failure (seller agent error: ACP request 3 failed: {"code":-32000,"message":"Authentication required"}), not a containment/launcher fault — sign in to the agent CLI on this machine (e.g. run `claude`, then `/login`), then restart +04:53:05Z seller node pre-advertise probe: serving 0/1 configured harness(es) +seller node prove-before-advertise: none of 1 configured harness(es) produced a probe artifact; refusing to advertise (fix the harness/launcher, then restart)