mirror of
https://github.com/joinmarket-webui/jam.git
synced 2026-08-13 12:33:26 +02:00
* chore(regtest): initial joinmarket-ng backend for regtest environment Adds jmwalletd, directory servers, makers, and bitcoind to the regtest docker-compose environment so jam can be developed against the jm-ng backend end-to-end. chore(regtest): wire docker compose env and vite dev proxy for jm-ng Connects the local regtest containers into a single compose network, adds tor and fund-wallet helpers, and points Vite's dev proxy at the local jmwalletd instance. fix(tags): recognize cj-change, used-empty, and flagged UTXO statuses joinmarket-ng's WalletService emits four statuses jam's JmPlainTagValue didn't cover: 'cj-change' (deanonymising change from our CJ), 'used-empty' (previously-used address with zero balance), and 'flagged' (address shared in a CJ that later failed). They fell through to the generic 'default' badge with no distinguishing styling. Add them to JmPlainTagValue and the status->variant map. 'used-empty' maps to the existing 'used' variant, 'flagged' to 'reused' (both signal a used address the user should avoid reusing). Add a dedicated 'cj-change' badge variant (emerald) next to 'cj-out' so the deanonymising change output is visually distinct from an equal-amount CJ output. fix(send): keep direct send available with maker Only block collaborative sends while the maker service is running. Direct sends stay enabled and the form explains why the collaborative toggle is unavailable. Changelog: Allow direct sends while the maker service is running docs(dev): explain jm-ng backend workflow fix(auth): only clear auth on invalid_token 401s Previously every 401 response cleared the session and logged the user out. jm-ng returns 401 for service-state errors (e.g. POST /tumbler/stop when nothing is running), which should never drop the session. Inspect the WWW-Authenticate header and only clear auth when the server signals ``error="invalid_token"``. Changelog: Non-auth 401 responses no longer log the user out. fix(dev): proxy /jmws to the jm-ng HTTPS port jm-ng serves WebSocket on the same port as the HTTPS API. The `jm-ng:dev` script used to override JMWALLETD_WEBSOCKET_PORT to 32283, but the regtest stack exposes that port without a matching listener, so vite proxied WebSocket upgrades into a dead port and the handshake returned empty. Default the env var to JMWALLETD_API_PORT and drop the override. Changelog: Fix WebSocket proxying in the local jm-ng dev setup. fix(dev): correct jm-ng websocket port and proxy targets Update the README and developer docs to reflect that both ports must be set when overriding for a custom jm-ng setup. fix(dev): correct jm-ng websocket port (#1237) chore(dev): distinct server configs for various backends * chore(regtest): address jm-ng review comments --------- Co-authored-by: m0wer <m0wer@sgn.space> Co-authored-by: Parth Bandwal <143504541+parrth20@users.noreply.github.com>
238 lines
7.3 KiB
Bash
238 lines
7.3 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# A script containing many useful functions and common operations.
|
|
# This file is suitable for sourcing into other scripts.
|
|
|
|
setup_colors() {
|
|
if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
|
|
NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
|
|
else
|
|
NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
|
|
fi
|
|
}
|
|
|
|
# setup colors to enable using `msg_*` functions immediately
|
|
setup_colors
|
|
|
|
msg() {
|
|
echo >&2 -e "${1-}"
|
|
}
|
|
|
|
msg_success() {
|
|
msg "${GREEN}${1-}${NOFORMAT}"
|
|
}
|
|
|
|
msg_warn() {
|
|
msg "${ORANGE}${1-}${NOFORMAT}"
|
|
}
|
|
|
|
msg_error() {
|
|
msg "${RED}${1-}${NOFORMAT}"
|
|
}
|
|
|
|
msg_highlight() {
|
|
msg "${YELLOW}${1-}${NOFORMAT}"
|
|
}
|
|
|
|
die() {
|
|
local code=${2-1} # default exit status 1
|
|
msg_error "${1-}"
|
|
exit "$code"
|
|
}
|
|
|
|
### Check if dependencies are installed.
|
|
################################################################################
|
|
if ! command -v curl &> /dev/null; then
|
|
die "This script needs 'curl' to run. Consider installing it."
|
|
fi
|
|
if ! command -v jq &> /dev/null; then
|
|
die "This script needs 'jq' to run. Consider installing it."
|
|
fi
|
|
if ! command -v docker &> /dev/null; then
|
|
die "This script needs 'docker' to run. Consider installing it."
|
|
fi
|
|
|
|
is_docker_container_running() {
|
|
[ -z "${1-}" ] && die "is_docker_container_running: Missing required parameter: name"
|
|
local result; result=$(docker ps --filter "name=^/${1-}$" --filter status=running -q)
|
|
echo "$result"
|
|
}
|
|
|
|
# --------------------------
|
|
# Unlock existing wallet
|
|
# --------------------------
|
|
## API: POST /api/v1/wallet/$wallet_name/unlock
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "walletname": "Satoshi.jmdat",
|
|
## "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
|
|
## }
|
|
unlock_wallet() {
|
|
local base_url; base_url=${1:-}
|
|
local wallet_name; wallet_name=${2:-}
|
|
local wallet_password; wallet_password=${3:-}
|
|
local unlock_request_payload; unlock_request_payload="{\"password\":\"$wallet_password\"}"
|
|
|
|
# param "--insecure": Is needed because a self-signed certificate is used in joinmarket regtest container
|
|
# param "--silent": Don't show progress meter or error messages (errors are reactivated with "--show-error").
|
|
# param "--show-error": When used with -s, --silent, it makes curl show an error message if it fails.
|
|
local unlock_result; unlock_result="$(curl "$base_url/api/v1/wallet/$wallet_name/unlock" --silent --show-error --insecure -H "Content-Type: application/json" --data "$unlock_request_payload" | jq ".")"
|
|
|
|
local unlock_result_error_msg; unlock_result_error_msg=$(jq -r '. | select(.message != null) | .message' <<< "$unlock_result")
|
|
if [ "$unlock_result_error_msg" != "" ]; then
|
|
die "$unlock_result_error_msg"
|
|
fi
|
|
|
|
echo "$unlock_result"
|
|
}
|
|
|
|
# --------------------------
|
|
# Lock wallet
|
|
# --------------------------
|
|
## API: GET /api/v1/wallet/$wallet_name/lock
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "walletname": "Satoshi.jmdat",
|
|
## "already_locked": false
|
|
## }
|
|
lock_wallet() {
|
|
local base_url; base_url=${1:-}
|
|
local auth_header; auth_header=${2:-}
|
|
local wallet_name; wallet_name=${3:-}
|
|
|
|
local lock_result; lock_result=$(curl "$base_url/api/v1/wallet/$wallet_name/lock" --silent --show-error --insecure -H "$auth_header" | jq ".")
|
|
|
|
local locked_wallet_name; locked_wallet_name=$(jq -r '.walletname' <<< "$lock_result")
|
|
[ "$locked_wallet_name" != "$wallet_name" ] && die "Problem while locking wallet $wallet_name"
|
|
|
|
echo "$lock_result"
|
|
}
|
|
|
|
# --------------------------
|
|
# Create new wallet
|
|
# --------------------------
|
|
## API: POST /api/v1/wallet/create
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "walletname": "Satoshi.jmdat",
|
|
## "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
|
## "seedphrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
## }
|
|
## or (if wallet already exists):
|
|
## 409 Conflict
|
|
## {
|
|
## "message": "Wallet file cannot be overwritten."
|
|
## }
|
|
create_wallet() {
|
|
local base_url; base_url=${1:-}
|
|
local wallet_name; wallet_name=${2:-}
|
|
local wallet_password; wallet_password=${3:-}
|
|
local create_request_payload; create_request_payload="{\"password\":\"$wallet_password\",\"walletname\":\"$wallet_name\",\"wallettype\":\"sw-fb\"}"
|
|
|
|
local create_result; create_result="$(curl "$base_url/api/v1/wallet/create" --silent --show-error --insecure -H "Content-Type: application/json" --data "$create_request_payload" | jq ".")"
|
|
|
|
local create_result_error_msg; create_result_error_msg=$(jq -r '. | select(.message != null) | .message' <<< "$create_result")
|
|
if [ "$create_result_error_msg" != "" ]; then
|
|
die "$create_result_error_msg"
|
|
fi
|
|
|
|
echo "$create_result"
|
|
}
|
|
|
|
# --------------------------
|
|
# Fetch new address
|
|
# --------------------------
|
|
## API: GET /api/v1/wallet/$wallet_name/address/new/$mixdepth
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
|
|
## }
|
|
fetch_new_address() {
|
|
local base_url; base_url=${1:-}
|
|
local auth_header; auth_header=${2:-}
|
|
local wallet_name; wallet_name=${3:-}
|
|
local mixdepth; mixdepth=${4:-}
|
|
|
|
local address_result; address_result=$(curl "$base_url/api/v1/wallet/$wallet_name/address/new/$mixdepth" --silent --show-error --insecure -H "$auth_header" | jq ".")
|
|
|
|
local address; address=$(jq -r '.address' <<< "$address_result")
|
|
|
|
echo "$address"
|
|
}
|
|
|
|
# --------------------------
|
|
# Fetch available wallets
|
|
# --------------------------
|
|
## API: GET /api/v1/wallet/all
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "wallets": ["Satoshi.jmdat", "test0.jmdat", "test1.jmdat", "test2.jmdat"]
|
|
## }
|
|
##
|
|
fetch_available_wallets() {
|
|
local base_url; base_url=${1:-}
|
|
|
|
local wallet_all_result; wallet_all_result="$(curl "$base_url/api/v1/wallet/all" --silent --show-error --insecure | jq ".")"
|
|
local available_wallets; available_wallets="$(jq -r '.wallets' <<< "$wallet_all_result")"
|
|
|
|
echo "$available_wallets"
|
|
}
|
|
|
|
# --------------------------
|
|
# Fetch Session Information
|
|
# --------------------------
|
|
## API: GET /api/v1/session
|
|
##
|
|
## Response:
|
|
## 200 OK
|
|
## {
|
|
## "session": false,
|
|
## "maker_running": false,
|
|
## "coinjoin_in_process": false,
|
|
## "wallet_name": "None"
|
|
## }
|
|
##
|
|
fetch_session() {
|
|
local base_url; base_url=${1:-}
|
|
local session_result; session_result="$(curl "$base_url/api/v1/session" --silent --show-error --insecure | jq ".")"
|
|
|
|
echo "$session_result"
|
|
}
|
|
|
|
# --------------------------
|
|
# Verify no open session
|
|
# --------------------------
|
|
verify_no_open_session_or_throw() {
|
|
local base_url; base_url=${1:-}
|
|
|
|
if session_result=$(fetch_session "$base_url"); then
|
|
msg_success "Successfully established connection to jmwalletd"
|
|
else rc=$?
|
|
die "Could not connect to joinmarket. Please make sure jmwalletd is running inside container."
|
|
fi
|
|
|
|
[ "$(jq -r '.session' <<< "$session_result")" != "false" ] && die "Please make sure no session is active."
|
|
local session_wallet_name; session_wallet_name=$(jq -r '.wallet_name' <<< "$session_result")
|
|
[ "$session_wallet_name" != "None" ] && [ "$session_wallet_name" != "" ] && die "Please make sure no wallet is active."
|
|
|
|
return 0
|
|
}
|
|
|
|
is_maker_running() {
|
|
local base_url; base_url=${1:-}
|
|
|
|
local session_result; session_result=$(fetch_session "$base_url")
|
|
local maker_running; maker_running=$(jq -r '.maker_running' <<< "$session_result")
|
|
|
|
echo "$maker_running"
|
|
}
|