Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Rebuild the Paykit fixtures on the paykit-rs version a Bitkit pull request pins with `scripts/follow-app-paykit`
- Withhold and restore the payment request issuer's endpoints with `POST /endpoints`, so a journey can make the app's request resolution fail and recover
- Route the apps' homeserver traffic through `homeserver-proxy`, whose control port (6298) delays or fails one identity's homeserver requests by path
- Route the apps' homeserver traffic through `homeserver-proxy`, whose control port (23298) delays or fails one identity's homeserver requests by path
- Hold LNURL-pay fixture callbacks with a `delay` mode, issue real invoices of the project's LND, and give a wallet a funded channel to it through LNURL-channel
- Issue payment requests payable through an LNURL-pay endpoint, with a separate proposal expiry

### Changed
- Publish every host port at 1024 or above, in the 23000-23999 range for the services Bitkit does not dial by a fixed number (README "Ports" lists them)
- Move the payment request fixture peers to paykit-rs rc65 on Pubky 0.14.0, the SDK current Bitkit builds pin (the `fixture-issuer` and `rc56-peer` service names stay)
- Move the marketplace fixture to the Pubky 0.14.0 homeserver, which grants the `LOCK` write locks current Bitkit builds take, with Paykit Server `0ffd4da` (pubky/paykit-server#46, paykit-rs rc65, locks-core rc8) and the driver on `@synonymdev/pubky` 0.14.0
- Show ready-to-copy settle and cancel commands in `holdinvoice` output
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ bitkit-docker/
This is a development/testing environment. Test manually using:

- `curl` commands for API endpoints
- Browser for UI pages at `http://localhost:3000`
- Browser for UI pages at `http://localhost:23000`
- Bitkit app for end-to-end testing (see README.md)

### Key Endpoints to Test
Expand Down
155 changes: 90 additions & 65 deletions README.md

Large diffs are not rendered by default.

63 changes: 34 additions & 29 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Host ports: every published port is 1024 or above. A port that Bitkit or the Pubky SDK dials by a fixed number keeps it
# (bitcoind 43782 and 39388, electrs 60001, homegate 6288, the Pubky testnet's 6881, 15411, 15412, 6286 and 6287, the
# Trezor Bridge's 21325 and 21328, Trezor User Env's controller 9001). Every other service publishes in 23000-23999,
# mostly at 23000 plus the last three digits of its container port. Services reach each other on their container
# ports. README "Ports" lists the map.
x-trezor-user-env-image: &trezor_user_env_image ghcr.io/trezor/trezor-user-env@sha256:15871ebb234bf7c0197cfcd31ddc2edbe90173cd6386fc0283fafe05bf888c06

services:
Expand Down Expand Up @@ -77,7 +82,7 @@ services:
expose:
- '80'
ports:
- '80:80'
- '23080:80'
volumes:
- './btc-fee-estimates.json:/www/btc-fee-estimates.json'
command:
Expand All @@ -94,15 +99,15 @@ services:
- '9735' # P2P
- '10009' # RPC
ports:
- '8080:8080'
- '9735:9735'
- '10009:10009'
- '23180:8080' # REST
- '23735:9735' # P2P
- '23009:10009' # RPC
volumes:
- './lnd:/home/lnd/.lnd/'
command:
- '--noseedbackup'
- '--alias=lnd'
- '--externalip=127.0.0.1'
- '--externalip=127.0.0.1:23735' # the host port, so the node URI a wallet reads dials the published P2P port
- '--bitcoin.active'
- '--bitcoin.regtest'
- '--bitcoin.node=bitcoind'
Expand All @@ -125,7 +130,7 @@ services:
expose:
- '3003'
ports:
- '3003:3003'
- '23003:3003'
environment:
HOST: '0.0.0.0'
PORT: '3003'
Expand All @@ -138,7 +143,7 @@ services:
environment:
- NODE_ENV=production
- PORT=3000
- DOMAIN=http://localhost:3000
- DOMAIN=http://localhost:23000
- DATABASE_URL=sqlite:///data/lnurl.db
# Bitcoin node connection
- BITCOIN_RPC_HOST=bitcoind
Expand All @@ -154,7 +159,7 @@ services:
- ./lnurl-server/data:/data
- ./lnd:/lnd-certs:ro
ports:
- "3000:3000"
- "23000:3000"

lnurl-server-fixture:
profiles: [lnurl-pay]
Expand All @@ -171,12 +176,12 @@ services:
LNURL_FIXTURE_DOMAIN: ${LNURL_FIXTURE_DOMAIN:-}
LND_REST_URL: https://lnd:8080
LND_DIR: /lnd
LND_P2P_ADDRESS: 127.0.0.1:9735
LND_P2P_ADDRESS: 127.0.0.1:23735
BITCOIN_RPC_URL: http://polaruser:polarpass@bitcoind:43782
volumes:
- ./lnd:/lnd:ro
ports:
- '3010:3010'
- '23010:3010'
healthcheck:
test: ['CMD', 'node', '-e', "fetch('http://127.0.0.1:3010/health').then(r => { if (!r.ok) process.exit(1); }).catch(() => process.exit(1))"]
interval: 5s
Expand All @@ -195,7 +200,7 @@ services:
- postgres_data:/var/lib/postgresql/data
- ./sql/v0_create_vss_db.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
- "23432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 10s
Expand All @@ -213,7 +218,7 @@ services:
volumes:
- homegate_postgres_data:/var/lib/postgresql/data
ports:
- "${HOMEGATE_POSTGRES_PORT:-5433}:5432"
- "${HOMEGATE_POSTGRES_PORT:-23433}:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d pubky_homegate"]
interval: 10s
Expand Down Expand Up @@ -251,7 +256,7 @@ services:
environment:
- SERVER_HOST=0.0.0.0
- SERVER_PORT=5005
- SERVER_DOMAIN=${DOMAIN:-http://localhost:5005}
- SERVER_DOMAIN=${DOMAIN:-http://localhost:23005}
- JWT_PRIVATE_KEY_PATH=/app/keys/private.pem
- JWT_PUBLIC_KEY_PATH=/app/keys/public.pem
- JWT_EXPIRES_IN=24h
Expand All @@ -265,7 +270,7 @@ services:
- ./lnurl-server/keys:/app/keys:ro
- lnurl_auth_data:/app/data
ports:
- "5005:5005"
- "23005:5005"
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://localhost:5005/health"]
interval: 30s
Expand Down Expand Up @@ -305,11 +310,11 @@ services:
volumes:
- ./vss-server-config.toml:/app/vss-server-config.toml:ro
ports:
- "5050:5050"
- "23050:5050"

# Wallet-backup VSS for a host that already runs other services. `vss-server`
# above stays the default stack's service (it also starts lnurl-auth-server).
# This profile starts only the server and its database, on 127.0.0.1:5050,
# This profile starts only the server and its database, on 127.0.0.1:23050,
# and checks RS256 JWTs with lnurl-server/keys/public.pem.
vss-postgres:
profiles: [vss]
Expand Down Expand Up @@ -350,7 +355,7 @@ services:
- ./vss-server-config.toml:/app/vss-server-config.toml:ro
- ./lnurl-server/keys/public.pem:/app/jwt-public.pem:ro
ports:
- "127.0.0.1:5050:5050"
- "127.0.0.1:23050:5050"

homegate:
container_name: homegate
Expand Down Expand Up @@ -390,9 +395,9 @@ services:
ports:
- "127.0.0.1:21325:21325" # Bridge legacy API used by Bitkit
- "127.0.0.1:21328:21328" # Bridge current API
- "127.0.0.1:9004:9001" # controller; avoids marketplace port 3001 seat blocks
- "127.0.0.1:9005:9002" # dashboard
- "127.0.0.1:6080:6080" # emulator screen / noVNC
- "127.0.0.1:23901:9001" # controller
- "127.0.0.1:23902:9002" # dashboard
- "127.0.0.1:23680:6080" # emulator screen / noVNC
volumes:
- trezor_emulator_state:/var/tmp
- trezor_emulator_logs:/trezor-user-env/logs
Expand All @@ -408,12 +413,12 @@ services:
image: *trezor_user_env_image
ports:
- "9001:9001" # User Env controller websocket
- "9002:9002" # User Env dashboard
- "23902:9002" # User Env dashboard
- "21325:21325" # Trezor Bridge legacy port used by Bitkit
- "21328:21328" # Trezor Bridge current port
- "15900:5900" # VNC port, offset to avoid local VNC conflicts
- "6080:6080" # noVNC web viewer
- "9003:9003" # MCP server SSE transport
- "23590:5900" # VNC
- "23680:6080" # noVNC web viewer
- "23903:9003" # MCP server SSE transport
environment:
- MACOS=1
- REGTEST_RPC_URL=http://host.docker.internal:43782
Expand Down Expand Up @@ -486,11 +491,11 @@ services:
- "127.0.0.1:15412:15412" # HTTP relay
- "127.0.0.1:6286:6286" # homeserver ICANN HTTP
- "127.0.0.1:6287:6297" # homeserver Pubky TLS, through homeserver-proxy (in-namespace clients reach the homeserver on 6287 directly)
- "127.0.0.1:6298:6298" # homeserver-proxy control: delay or fail one identity's requests
- "127.0.0.1:${MARKETPLACE_HOMESERVER_ADMIN_PORT:-16288}:6288" # homeserver admin (6288 is homegate's)
- "127.0.0.1:${MARKETPLACE_PAYKIT_PORT:-3001}:3001" # paykit-server, shares this namespace
- "127.0.0.1:3012:3012" # opt-in fixture-issuer, shares this namespace
- "127.0.0.1:3013:3013" # opt-in rc56-peer, shares this namespace
- "127.0.0.1:23298:6298" # homeserver-proxy control: delay or fail one identity's requests
- "127.0.0.1:${MARKETPLACE_HOMESERVER_ADMIN_PORT:-23288}:6288" # homeserver admin (6288 is homegate's)
- "127.0.0.1:${MARKETPLACE_PAYKIT_PORT:-23101}:3001" # paykit-server, shares this namespace
- "127.0.0.1:23012:3012" # opt-in fixture-issuer, shares this namespace
- "127.0.0.1:23013:3013" # opt-in rc56-peer, shares this namespace

# Paykit Server 0ffd4da, the head of pubky/paykit-server#46 (paykit-rs rc65, the version both apps pin; Pubky 0.14.0; move to its merge or
# release once #46 lands), built from source with the upstream
Expand Down
6 changes: 3 additions & 3 deletions docs/pubky-marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ these fixed ports:
| 6286, 6287 | homeserver (ICANN HTTP, Pubky TLS) |
| 15411, 15412 | PKARR relay, HTTP relay |
| 6881 (tcp and udp) | DHT bootstrap |
| 3001 | Paykit Server (`MARKETPLACE_PAYKIT_PORT`) |
| 3012, 3013 | `fixture-issuer` and `rc56-peer` of the opt-in `payment-requests` profile (see the README); nothing listens until they run |
| 16288 | homeserver admin (`MARKETPLACE_HOMESERVER_ADMIN_PORT`; the in-container 6288 is Homegate's host port) |
| 23101 | Paykit Server (`MARKETPLACE_PAYKIT_PORT`; 3001 in the container) |
| 23012, 23013 | `fixture-issuer` and `rc56-peer` of the opt-in `payment-requests` profile (see the README); nothing listens until they run |
| 23288 | homeserver admin (`MARKETPLACE_HOMESERVER_ADMIN_PORT`; the in-container 6288 is Homegate's host port) |
| 60001 | Electrum, from the base stack |

`paykit-server` and `marketplace-driver` share the `pubky-testnet` network namespace, as the upstream
Expand Down
11 changes: 5 additions & 6 deletions docs/trezor-emulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ docker compose --profile trezor-emulator exec -T trezor-emulator \
/trezor-user-env/.venv/bin/python3 /opt/bitkit-trezor/trezor-fixture-check.py
```

Ports: Bridge legacy `21325`, Bridge current `21328`, controller `9004`, dashboard
`9005`, and noVNC `6080`. The controller and dashboard are published on different
ports from the manual User Env to avoid collisions with the regtest services when
several projects use blocks of 1000 ports. Docker-backed simulator seats remap
Ports: Bridge legacy `21325`, Bridge current `21328`, controller `23901`, dashboard
`23902`, and noVNC `23680`. The controller is published on a different port from
the manual User Env's `9001`, which Bitkit's UI tests dial. Docker-backed simulator seats remap
these base ports and forward them to their seat's loopback address. Declare this
profile as an on-demand offer; run no global shared device instance.

Expand Down Expand Up @@ -105,7 +104,7 @@ Run Bitkit from Xcode on the relevant Trezor branch, then open `Settings -> Adva

The User Env dashboard and Bridge remain available at the same localhost endpoints:

- User Env dashboard: <http://localhost:9002>
- User Env dashboard: <http://localhost:23902>
- Trezor Bridge: <http://localhost:21325>

## Smoke Checklist
Expand All @@ -128,7 +127,7 @@ Use this checklist when reviewing any Bitkit app PR that needs the Trezor emulat
./scripts/trezor-emulator stop
```

Open the User Env dashboard at <http://localhost:9002>. Trezor Bridge listens at <http://localhost:21325>.
Open the User Env dashboard at <http://localhost:23902>. Trezor Bridge listens at <http://localhost:21325>.

## Troubleshooting

Expand Down
2 changes: 1 addition & 1 deletion lnurl-server/pay-fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ function createApp(env = process.env) {
const info = await lnd('GET', '/v1/getinfo');
const k1 = randomBytes(32).toString('hex');
channels.set(k1, { created_at: new Date().toISOString() });
res.json({ tag: 'channelRequest', uri: `${info.identity_pubkey}@${env.LND_P2P_ADDRESS || '127.0.0.1:9735'}`, callback: `${origin(req)}/channel/fixture/callback`, k1 });
res.json({ tag: 'channelRequest', uri: `${info.identity_pubkey}@${env.LND_P2P_ADDRESS || '127.0.0.1:23735'}`, callback: `${origin(req)}/channel/fixture/callback`, k1 });
} catch (err) {
next(err);
}
Expand Down
2 changes: 1 addition & 1 deletion marketplace/driver/driver.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ async function publicInfo() {
http_relay: 'http://localhost:15412',
dht: 'localhost:6881',
},
paykit_server: { url: 'http://localhost:3001', receiver_path: SERVER_PATH, network: 'regtest' },
paykit_server: { url: 'http://localhost:23101', receiver_path: SERVER_PATH, network: 'regtest' },
electrum: 'tcp://127.0.0.1:60001',
chain,
seller: fixture.seller
Expand Down
46 changes: 25 additions & 21 deletions payment-requests/prepare
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
set -euo pipefail
cd "$(dirname "$0")/.."

# The host ports compose publishes the two peers on; a project that remaps them sets these.
issuer_port="${FIXTURE_ISSUER_PORT:-23012}"
peer_port="${RC56_PEER_PORT:-23013}"

fail() {
echo "prepare: $*" >&2
exit 1
Expand All @@ -29,26 +33,26 @@ wait_health() {
done
}
deadline=$((SECONDS + 150))
wait_health 3012
wait_health 3013
wait_health "$issuer_port"
wait_health "$peer_port"

issuer=$(curl --fail-with-body -sS http://127.0.0.1:3012/info)
peer=$(curl --fail-with-body -sS http://127.0.0.1:3013/info)
issuer=$(curl --fail-with-body -sS http://127.0.0.1:$issuer_port/info)
peer=$(curl --fail-with-body -sS http://127.0.0.1:$peer_port/info)
to_peer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$peer")" --arg path "$(jq -r .receiver_path <<<"$peer")" '{peer_pubky:$pubky,peer_path:$path}')
to_issuer=$(jq -nc --arg pubky "$(jq -r .pubky <<<"$issuer")" --arg path "$(jq -r .receiver_path <<<"$issuer")" '{peer_pubky:$pubky,peer_path:$path}')

issuer_link=$(post 3012 /sync "$to_peer" 2>/dev/null || true)
peer_link=$(post 3013 /sync "$to_issuer" 2>/dev/null || true)
issuer_link=$(post "$issuer_port" /sync "$to_peer" 2>/dev/null || true)
peer_link=$(post "$peer_port" /sync "$to_issuer" 2>/dev/null || true)
if ! jq -e '.link == "linked"' <<<"$issuer_link" >/dev/null 2>&1; then
post 3012 /link "$(jq -c '. + {mode:"initiate"}' <<<"$to_peer")" >/dev/null || fail 'issuer link initiate failed'
post "$issuer_port" /link "$(jq -c '. + {mode:"initiate"}' <<<"$to_peer")" >/dev/null || fail 'issuer link initiate failed'
fi
if ! jq -e '.link == "linked"' <<<"$peer_link" >/dev/null 2>&1; then
post 3013 /link "$(jq -c '. + {mode:"accept"}' <<<"$to_issuer")" >/dev/null || fail 'peer link accept failed'
post "$peer_port" /link "$(jq -c '. + {mode:"accept"}' <<<"$to_issuer")" >/dev/null || fail 'peer link accept failed'
fi
linked=false
for _ in {1..20}; do
issuer_link=$(post 3012 /sync "$to_peer")
peer_link=$(post 3013 /sync "$to_issuer")
issuer_link=$(post "$issuer_port" /sync "$to_peer")
peer_link=$(post "$peer_port" /sync "$to_issuer")
if jq -e '.link == "linked"' <<<"$issuer_link" >/dev/null &&
jq -e '.link == "linked"' <<<"$peer_link" >/dev/null; then
linked=true
Expand All @@ -64,7 +68,7 @@ fi
issue() {
local reference="$1" amount="$2" extra="${3:-}" response
if [ -z "$extra" ]; then extra='{}'; fi
response=$(post 3012 /request "$(jq -nc --argjson peer "$to_peer" --argjson extra "$extra" --arg ref "$reference" --argjson sats "$amount" '$peer + $extra + {reference:$ref,amount_sats:$sats}')") || return 1
response=$(post "$issuer_port" /request "$(jq -nc --argjson peer "$to_peer" --argjson extra "$extra" --arg ref "$reference" --argjson sats "$amount" '$peer + $extra + {reference:$ref,amount_sats:$sats}')") || return 1
jq -er .payment_request_id <<<"$response" || {
echo "prepare: no payment_request_id issuing $reference: $response" >&2
return 1
Expand All @@ -75,32 +79,32 @@ record() {
jq -nc --argjson peer "$to_issuer" --arg id "$1" '$peer + {payment_request_id:$id}'
}

receive() { post 3013 /sync "$to_issuer" >/dev/null; }
sync_issuer() { post 3012 /sync "$to_peer" >/dev/null; }
receive() { post "$peer_port" /sync "$to_issuer" >/dev/null; }
sync_issuer() { post "$issuer_port" /sync "$to_peer" >/dev/null; }

proposed=$(issue rc56-proposed 10000)
receive

accepted=$(issue rc56-accepted 11000)
receive
post 3013 /accept "$(record "$accepted")" >/dev/null
post "$peer_port" /accept "$(record "$accepted")" >/dev/null
sync_issuer

rejected=$(issue rc56-rejected 12000)
receive
post 3013 /reject "$(record "$rejected")" >/dev/null
post "$peer_port" /reject "$(record "$rejected")" >/dev/null
sync_issuer

canceled=$(issue rc56-canceled 13000)
receive
post 3013 /cancel "$(record "$canceled")" >/dev/null
post "$peer_port" /cancel "$(record "$canceled")" >/dev/null
sync_issuer

completed=$(issue rc56-completed 14000)
receive
post 3013 /accept "$(record "$completed")" >/dev/null
post "$peer_port" /accept "$(record "$completed")" >/dev/null
sync_issuer
paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$completed")" --arg address "$(jq -r .address <<<"$issuer")" '$record + {address:$address,amount_sats:14000}')")
paid=$(post "$peer_port" /pay "$(jq -nc --argjson record "$(record "$completed")" --arg address "$(jq -r .address <<<"$issuer")" '$record + {address:$address,amount_sats:14000}')")
jq -e '.proof_sent == true' <<<"$paid" >/dev/null || fail "request $completed: /pay did not deliver the proof: $paid"
sync_issuer

Expand All @@ -122,9 +126,9 @@ PY
monthly_extra=$(jq -nc --arg start "$(jq -r .start <<<"$period")" '{monthly_starts_at:$start,period_start_deadline_seconds:3456000}')
monthly_accepted=$(issue rc56-monthly-accepted 15000 "$monthly_extra")
receive
post 3013 /accept "$(record "$monthly_accepted")" >/dev/null
post "$peer_port" /accept "$(record "$monthly_accepted")" >/dev/null
sync_issuer
monthly_paid=$(post 3013 /pay "$(jq -nc --argjson record "$(record "$monthly_accepted")" --arg address "$(jq -r .address <<<"$issuer")" --arg start "$(jq -r .start <<<"$period")" --arg end "$(jq -r .end <<<"$period")" '$record + {address:$address,amount_sats:15000,billing_period_start:$start,billing_period_end:$end}')")
monthly_paid=$(post "$peer_port" /pay "$(jq -nc --argjson record "$(record "$monthly_accepted")" --arg address "$(jq -r .address <<<"$issuer")" --arg start "$(jq -r .start <<<"$period")" --arg end "$(jq -r .end <<<"$period")" '$record + {address:$address,amount_sats:15000,billing_period_start:$start,billing_period_end:$end}')")
jq -e '.proof_sent == true' <<<"$monthly_paid" >/dev/null || fail "request $monthly_accepted: /pay did not deliver the proof: $monthly_paid"
sync_issuer
monthly_proposed=$(issue rc56-monthly-proposed 16000 "$monthly_extra")
Expand All @@ -136,7 +140,7 @@ for txid in "$(jq -r .txid <<<"$paid")" "$(jq -r .txid <<<"$monthly_paid")"; do
jq -e '.confirmations >= 1' >/dev/null || fail "transaction $txid is not confirmed after mining one block"
done

records=$(post 3013 /records "$to_issuer")
records=$(post "$peer_port" /records "$to_issuer")
check() {
local id="$1" expected="$2" actual
jq -e --arg id "$id" --arg expected "$expected" '.records[] | select(.payment_request_id == $id and .state == $expected)' <<<"$records" >/dev/null && return 0
Expand Down
2 changes: 1 addition & 1 deletion payment-requests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ struct RequestInput {
deadline_at: Option<String>,
monthly_starts_at: Option<String>,
period_start_deadline_seconds: Option<u64>,
// an LNURL-pay string (`GET :3010/generate/pay` of the lane's LNURL fixture): the request then accepts only `btc-lightning-lnurl`,
// an LNURL-pay string (`GET :23010/generate/pay` of the lane's LNURL fixture): the request then accepts only `btc-lightning-lnurl`,
// which the private payment list sent with it offers (bitkit-android#1401 J19, 7 Oct)
lnurl: Option<String>,
// when the proposal itself expires (the acceptance deadline), apart from the payment deadline
Expand Down
Loading