From 780c3122bd2ba27fc753b181449ff116ede8b967 Mon Sep 17 00:00:00 2001 From: Renato Britto Date: Sun, 5 Apr 2026 20:16:34 -0300 Subject: [PATCH] feat(docs): add docs for api package --- .gitignore | 4 +- README.md | 97 ++++++++++++++++++++++++++++++++------- api/README.md | 107 +++++++++++++++++++++++++++++++++++++++++++ bitcoin.conf.example | 8 ++++ 4 files changed, 198 insertions(+), 18 deletions(-) create mode 100644 api/README.md create mode 100644 bitcoin.conf.example diff --git a/.gitignore b/.gitignore index 09a9ffd..8e9d282 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ dist/ .qwen **/__pycache__/ target/ -Cargo.lock \ No newline at end of file +Cargo.lock +bitcoin.conf +.bitcoin-regtest diff --git a/README.md b/README.md index 1e9896e..906d65d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Stealth ships a Rust workspace with: - `stealth-engine` (analysis engine) - `stealth-model` (domain model types and interfaces) +- `stealth-api` (http api) - `stealth-bitcoincore` (Bitcoin Core RPC gateway adapter) ## Project Direction @@ -162,36 +163,94 @@ Stealth currently runs **12 detectors** in `stealth-engine`. ```bash git clone https://github.com/stealth-bitcoin/stealth.git cd stealth +cargo build ``` -### 2. Configure blockchain connection +### 2. Configure Bitcoin Core RPC (regtest) -Edit: - -``` -backend/script/config.ini -``` - -### 3. Development setup (regtest) - -A regtest environment is provided for development and reproducible testing of heuristics. +Create a local `bitcoin.conf`: ```bash -cd backend/script -./setup.sh +cat > bitcoin.conf <<'EOF' +regtest=1 +server=1 +daemon=1 +txindex=1 +listen=0 +[regtest] +rpcbind=127.0.0.1 +rpcallowip=127.0.0.1 +rpcuser=localuser +rpcpassword=localpass +rpcport=18443 +fallbackfee=0.0002 +EOF ``` -### 4. Generate sample transactions +### 3. Start Bitcoin Core + +Regtest example: ```bash -python3 reproduce.py +mkdir -p "$PWD/.bitcoin-regtest" +bitcoind -datadir="$PWD/.bitcoin-regtest" -conf="$PWD/bitcoin.conf" -daemon ``` -### 5. Start backend +Mainnet example: ```bash -cd backend/src/StealthBackend -./mvnw quarkus:dev +bitcoind -daemon +``` + +### 4. Start the API + +```bash +STEALTH_RPC_URL=http://127.0.0.1:18443 \ +STEALTH_RPC_USER=localuser \ +STEALTH_RPC_PASS=localpass \ + cargo run --bin stealth-api +``` + +`stealth-api` auto-detects common local RPC ports and can use credentials from `bitcoin.conf`, cookie file, or env vars. + +### 5. Run a usable scan request + +```bash +DATADIR="$PWD/.bitcoin-regtest" +CONF="$PWD/bitcoin.conf" +RPC="bitcoin-cli -datadir=$DATADIR -conf=$CONF -regtest -rpcport=18443" +API_PORT=20899 + +mkdir -p "$DATADIR" +if ! $RPC getblockchaininfo >/dev/null 2>&1; then + bitcoind -datadir="$DATADIR" -conf="$CONF" -daemon +fi + +for _ in $(seq 1 100); do + if $RPC getblockchaininfo >/dev/null 2>&1; then + break + fi + sleep 0.2 +done + +STEALTH_RPC_URL=http://127.0.0.1:18443 \ +STEALTH_RPC_USER=localuser \ +STEALTH_RPC_PASS=localpass \ +STEALTH_API_BIND=127.0.0.1:$API_PORT \ + cargo run --bin stealth-api >/tmp/stealth-api.log 2>&1 & +API_PID=$! +trap 'kill $API_PID >/dev/null 2>&1 || true' EXIT + +WALLET="scanwallet_$(date +%s)" +$RPC createwallet "$WALLET" >/dev/null +TARGET_ADDR="$($RPC -rpcwallet="$WALLET" getnewaddress)" +$RPC generatetoaddress 101 "$TARGET_ADDR" >/dev/null + +DESC="$($RPC -rpcwallet="$WALLET" getaddressinfo "$TARGET_ADDR" | jq -r '.desc')" + +curl -s "http://127.0.0.1:$API_PORT/api/wallet/scan" -H 'content-type: application/json' -d "{\"descriptor\":\"$DESC\"}" | jq + +$RPC stop ``` ### 6. Start frontend @@ -232,6 +291,10 @@ stealth/ │ │ └── bitcoin-data/ # Regtest chain data (gitignored) │ └── src/StealthBackend/ # Quarkus Java REST API (single /api/wallet/scan endpoint) └── slides/ # Slidev pitch presentation +├── api/ # stealth-api (Axum HTTP layer) +│ ├── src/ +│ └── tests/ +└── target/ # Cargo build outputs ``` ### Test Coverage diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..0586d6b --- /dev/null +++ b/api/README.md @@ -0,0 +1,107 @@ +# Stealth API + +`stealth-api` is the Rust HTTP transport layer for Stealth. It connects to a +running `bitcoind` via JSON-RPC, imports descriptors into temporary wallets, +builds a transaction graph, and runs privacy detectors from +`stealth-engine`. + +## Running + +```bash +# Stop any old API process, then start the current source build +pkill -f 'target/debug/stealth-api' 2>/dev/null || true + +# Auto-detects local bitcoind RPC port (prefers 18443, then 8332/18332/38332) +# and uses credentials from bitcoin.conf or local cookie files. +cargo run --bin stealth-api +``` + +Set auth explicitly with username/password: + +```bash +STEALTH_RPC_URL=http://127.0.0.1:8332 \ +STEALTH_RPC_USER=user \ +STEALTH_RPC_PASS=pass \ + cargo run --bin stealth-api +``` + +Or use a cookie file: + +```bash +STEALTH_RPC_URL=http://127.0.0.1:8332 \ +STEALTH_RPC_COOKIE=~/.bitcoin/.cookie \ + cargo run --bin stealth-api +``` + +Configure the listen address with `STEALTH_API_BIND` (default `127.0.0.1:20899`). + +If you see `Connection refused (os error 111)`, either: +1. an old `stealth-api` process is still running, or +2. `bitcoind` RPC is not reachable on the detected/configured URL. + +## API + +### `POST /api/wallet/scan` + +Accepts one mutually-exclusive source: + +| Field | Type | Description | +|-------|------|-------------| +| `descriptor` | `string` | Single output descriptor | +| `descriptors` | `string[]` | Multiple descriptors | +| `utxos` | `UtxoInput[]` | Raw UTXO set | + +**Descriptor scan flow:** creates a blank watch-only wallet, imports the +descriptor(s) with a full blockchain rescan, builds a `TxGraph`, runs all +17 detectors, then cleans up the temporary wallet. + +**UTXO scan flow:** resolves each UTXO's address from the node, builds a +partial transaction graph, and runs applicable detectors. + +#### Example (real descriptor from Bitcoin Core) + +```bash +RPC="bitcoin-cli -regtest -rpcport=18443 -rpcuser=localuser -rpcpassword=localpass" +WALLET="scanwallet_$(date +%s)" + +$RPC createwallet "$WALLET" >/dev/null +ADDR="$($RPC -rpcwallet="$WALLET" getnewaddress)" +DESC="$($RPC -rpcwallet="$WALLET" getaddressinfo "$ADDR" | jq -r '.desc')" + +curl 'http://localhost:20899/api/wallet/scan' \ + -H 'content-type: application/json' \ + -d "{\"descriptor\":\"$DESC\"}" | jq +``` + +#### Responses + +| Status | Meaning | +|--------|---------| +| `200` | Scan completed — body is a `Report` | +| `400` | Invalid input (bad descriptor shape, empty UTXOs, …) | +| `502` | bitcoind RPC unavailable/auth failed/connection failed | + +## Environment variables + +| Variable | Description | +|----------|-------------| +| `STEALTH_API_BIND` | Listen address (default `127.0.0.1:20899`) | +| `STEALTH_RPC_URL` | bitcoind RPC endpoint (overrides auto-detection) | +| `STEALTH_RPC_USER` | RPC username (otherwise read from `bitcoin.conf` when available) | +| `STEALTH_RPC_PASS` | RPC password (otherwise read from `bitcoin.conf` when available) | +| `STEALTH_RPC_COOKIE` | Path to `.cookie` file (otherwise API auto-detects common local cookie locations) | + +## E2E test (regtest) + +The API includes an end-to-end regtest integration test that: +1. creates wallets, +2. gets a real descriptor from `bitcoind`, +3. scans once with no history (`summary.clean = true`), +4. creates/mine transactions, +5. scans again and asserts findings (`summary.clean = false`). + +Run it with: + +```bash +cargo test -p stealth-api scan_descriptor_clean_then_findings_after_regtest_activity -- --nocapture +``` diff --git a/bitcoin.conf.example b/bitcoin.conf.example new file mode 100644 index 0000000..ba9dbf1 --- /dev/null +++ b/bitcoin.conf.example @@ -0,0 +1,8 @@ +server=1 +rpcuser=localuser +rpcpassword=localpass + +[regtest] +rpcbind=127.0.0.1 +rpcallowip=127.0.0.1 +rpcport=18443