Compare commits

..

No commits in common. "master" and "v0.8.4" have entirely different histories.

28 changed files with 260 additions and 1175 deletions

View file

@ -1,15 +0,0 @@
# EditorConfig helps maintain consistent coding styles
# https://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab

View file

@ -1,65 +0,0 @@
name: amd64-image-test
concurrency:
group: amd64-image-test-${{ github.event.workflow_run.id || inputs.run_id || github.run_id }}
cancel-in-progress: true
on:
workflow_run:
workflows: ["amd64-image-build"]
types: [completed]
workflow_dispatch:
inputs:
run_id:
description: "amd64-image-build workflow run ID to test"
required: true
type: string
permissions:
actions: read
contents: read
jobs:
bats-image-test:
name: Run Bats against amd64 image artifact
runs-on: ubuntu-22.04
timeout-minutes: 240
if: ${{ github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository) }}
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.event.workflow_run.head_repository.full_name || github.repository }}
ref: ${{ github.event.workflow_run.head_sha || github.ref }}
- name: Check out bats-core
uses: actions/checkout@v4
with:
repository: bats-core/bats-core
# bats-core v1.12.0
ref: 713504bc0224a19b3d7c7958c18dc07f64f54b44
path: .bats-core
persist-credentials: false
- name: Download amd64 image artifact
uses: actions/download-artifact@v4
with:
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id || inputs.run_id }}
pattern: joininbox-amd64-image-*
path: artifacts
merge-multiple: true
- name: Verify and decompress image
shell: bash
run: |
set -euo pipefail
cd artifacts
sha256sum -c joininbox-amd64-debian.qcow2.gz.sha256
gzip -dk joininbox-amd64-debian.qcow2.gz
sha256sum -c joininbox-amd64-debian.qcow2.sha256
- name: Run image Bats tests
timeout-minutes: 30
run: ci/amd64/test.amd64-image-bats.sh "${GITHUB_WORKSPACE}/artifacts/joininbox-amd64-debian.qcow2"

View file

@ -1,60 +0,0 @@
name: Packer Syntax Check
concurrency:
group: packer-syntax-check-${{ github.head_ref || github.ref }}
cancel-in-progress: true
on:
push:
branches: [ "master" ]
paths:
- '.github/workflows/packer-syntax-check.yml'
- 'ci/amd64/**/*.pkr.hcl'
- 'ci/amd64/**/*.pkrvars.hcl'
- 'ci/amd64/packer.build.amd64-debian.sh'
- 'ci/arm64-rpi/**/*.pkr.hcl'
- 'ci/arm64-rpi/**/*.pkrvars.hcl'
- 'ci/arm64-rpi/arm64-rpi.sh'
pull_request:
branches: [ "master" ]
paths:
- '.github/workflows/packer-syntax-check.yml'
- 'ci/amd64/**/*.pkr.hcl'
- 'ci/amd64/**/*.pkrvars.hcl'
- 'ci/amd64/packer.build.amd64-debian.sh'
- 'ci/arm64-rpi/**/*.pkr.hcl'
- 'ci/arm64-rpi/**/*.pkrvars.hcl'
- 'ci/arm64-rpi/arm64-rpi.sh'
workflow_dispatch:
jobs:
validate-packer-syntax:
name: Validate Packer Syntax
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Packer
uses: hashicorp/setup-packer@v3
- name: Show Packer version
run: packer version
- name: Validate amd64 templates (syntax only)
shell: bash
run: |
set -euo pipefail
mapfile -t templates < <(find ci/amd64 ci/arm64-rpi -type f \( -name '*.pkr.hcl' -o -name '*.pkrvars.hcl' \) | sort)
if [ ${#templates[@]} -eq 0 ]; then
echo "No Packer templates found under ci/amd64 or ci/arm64-rpi"
exit 1
fi
for template in "${templates[@]}"; do
echo "Validating $template"
packer validate -syntax-only "$template"
done

70
FAQ.md
View file

@ -30,7 +30,6 @@
- [Wallet recovery](#wallet-recovery)
- [on JoininBox](#on-joininbox)
- [on the remote node](#on-the-remote-node)
- [Migrating from legacy wallet.dat to descriptor wallet](#migrating-from-legacy-walletdat-to-descriptor-wallet)
- [USB SSD recommendation](#usb-ssd-recommendation)
- [Pruned node notes](#pruned-node-notes)
- [External drive](#external-drive)
@ -196,7 +195,7 @@ Use `ssh` with `torsocks` on the desktop (needs Tor installed):
server=1
daemon=1
disablewallet=0
main.wallet=watch-only-descriptor-wallet
main.wallet=wallet.dat
# Connection settings
rpcuser=REDACTED
@ -429,7 +428,7 @@ JoinMarket docs:
* https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/USAGE.md#recover
### on JoininBox
* Connect the remote bitcoind with `CONFIG` -> `CONNECT` menu so it checks if the connection is successful. It will also set the remote watch-only-descriptor-wallet in bitcoind to "joininbox" so will need to rescan that after recovering an old wallet with previously used addresses.
* Connect the remote bitcoind with `CONFIG` -> `CONNECT` menu so it checks if the connection is successful. It will also set the remote watch-only wallet in bitcoind to "joininbox" so will need to rescan that after recovering an old wallet with previously used addresses.
* When using the CLI and connecting to the remote node over Tor, you will need to use the script with the torsocks prefix like:
`torsocks python3 wallet-tool.py --recoversync -g 20 ~/.joinmarket/wallets/wallet.jmdat`
@ -438,7 +437,7 @@ JoinMarket docs:
* Use the menu option `WALLET` -> `RESCAN` or follow manually
* the wallet defined as
`rpc_wallet =`
in the joinmarket.cfg is the wallet which is used as a watch-only-descriptor-wallet in the remote bitcoind.
in the joinmarket.cfg is the wallet which is used as watch only in the remote bitcoind.
You need to run rescanblockchain on that wallet in bitcoind after importing the joinmarket wallet.
* The wallet is set in the joinmarket.cfg (by default called `joininbox` should show up when you run:
`bitcoin-cli listwallets`
@ -450,65 +449,6 @@ Rescanning from the first SegWit block is sufficient for the default SegWit wall
* Monitor progress (on a RaspiBlitz):
`sudo tail -fn 100 /mnt/hdd/app-storage/bitcoin/debug.log`
Once the rescan is finished you balances should appear in the `INFO` menu (`wallet-tool.py`)
## Migrating from legacy wallet.dat to descriptor wallet
Starting with the 0.9.0 version, JoininBox uses Bitcoin Core's descriptor wallets (`watch-only-descriptor-wallet`) instead of the legacy `wallet.dat`. This change provides better compatibility with modern Bitcoin Core versions (v26+) and aligns with Bitcoin Core's default wallet format.
### Why this change?
Bitcoin Core has deprecated BDB (Berkeley DB) wallets in favor of descriptor wallets. The new descriptor wallets:
- Are the default in Bitcoin Core v26+
- Don't require the `deprecatedrpc=create_bdb` configuration
- Have better performance and features
- Are actively maintained and improved
### How automatic migration works
When `rpc_wallet_file` is still set to `wallet.dat`, JoininBox queries the connected Bitcoin Core node using RPC before changing the configuration:
- Bitcoin Core v30.0 and newer: JoininBox atomically changes `rpc_wallet_file` to `watch-only-descriptor-wallet`, creates or loads that descriptor wallet, and displays the migration notice when the old `wallet.dat` is present.
- Bitcoin Core v29.x and earlier: JoininBox keeps using `wallet.dat` and does not start automatic migration.
- Version unavailable: JoininBox leaves `wallet.dat` configured rather than migrating without confirming compatibility.
The migration does not rename, modify, or delete the old `wallet.dat`. It changes which Bitcoin Core wallet JoinMarket uses for watch-only address imports and transaction history.
### Migration steps for existing users
If you're upgrading from a previous version of JoininBox that used `wallet.dat`, follow these steps:
1. **The migration notice will appear automatically on Bitcoin Core v30.0 and newer**
When you first use any wallet-related function after updating, JoininBox checks the connected Bitcoin Core version. On v30.0 and newer it switches the configured RPC wallet to `watch-only-descriptor-wallet`, detects the old `wallet.dat`, and displays a migration notice. Bitcoin Core v29.x and earlier continue using `wallet.dat` without starting the automatic migration.
2. **Open each JoinMarket wallet**
Go to `WALLET` -> `DISPLAY` and open each of your JoinMarket wallets (`.jmdat` files) at least once. This imports the addresses into the new `watch-only-descriptor-wallet` in Bitcoin Core.
3. **Run a blockchain rescan**
After opening all your wallets, go to `WALLET` -> `RESCAN` and enter a blockheight:
- Use `481824` (first SegWit block) for wallets created after August 2017
- Can use a later blockheight if you know when your wallet had its first deposit
4. **Wait for the rescan to complete**
The rescan can take several hours depending on the blockheight and your hardware. Monitor progress with:
```bash
# On standalone JoininBox:
sudo tail -f /home/bitcoin/.bitcoin/debug.log
# On RaspiBlitz:
sudo tail -f /mnt/hdd/bitcoin/debug.log
```
5. **Verify your balances**
Once the rescan completes, check your wallet balances with `WALLET` -> `DISPLAY`.
### Notes
- The old `wallet.dat` is not deleted and remains in Bitcoin Core
- Automatic migration only runs when the connected Bitcoin Core version is v30.0 or newer
- You only need to perform this migration once
- The migration notice will not appear again after you acknowledge it
- If you have issues, you can reset the migration flag by removing `walletMigrationDone=true` from `/home/joinmarket/joinin.conf`
## USB SSD recommendation
**JoininBox operates on the minimum viable hardware under the assumption that the seed (and passphrase) of the wallets used is safely backed up and can be recovered fully**
* The above warning is especially true for SDcard as they fail often, use a good quality one.
@ -579,7 +519,7 @@ Alternatively to a pruned node there could be a larger >400 GB storage connected
# -rw------- 1 bitcoin bitcoin 1521305 Mar 21 10:38 peers.dat
# -rw-r--r-- 1 bitcoin bitcoin 7 Mar 21 10:08 settings.json
# drwx------ 34 bitcoin bitcoin 4096 Dec 7 23:39 specter
# drwx------ 2 bitcoin bitcoin 4096 Mar 21 10:38 watch-only-descriptor-wallet
# drwx------ 2 bitcoin bitcoin 4096 Mar 21 10:38 wallet.dat
installMainnet
...
# # OK - the bitcoind.service is now enabled
@ -588,7 +528,7 @@ Alternatively to a pruned node there could be a larger >400 GB storage connected
#
# # Monitor the bitcoind with: sudo tail -f /home/bitcoin/.bitcoin/mainnet/debug.log
#
# # Create watch-only-descriptor-wallet ...
# # Create wallet.dat ...
# error code: -28
# error message:
# Loading block index...

View file

@ -23,7 +23,7 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
- [Features](#features)
- [Required Hardware](#required-hardware)
- [A computer running a Debian / Ubuntu Linux flavour.](#a-computer-running-a-debian--ubuntu-linux-flavour)
- [RaspberryPi 5 or 4](#raspberrypi-5-or-4)
- [RaspberryPi 4 or 3](#raspberrypi-4-or-3)
- [VPS eg: host4coins.net](#vps-eg-host4coinsnet)
- [Set up using an SDcard image](#set-up-using-an-sdcard-image)
- [Set up JoininBox on Linux](#set-up-joininbox-on-linux)
@ -32,6 +32,7 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
- [More info](#more-info)
- [About JoinMarket](#about-joinmarket)
- [Forums](#forums)
- [Donations](#donations)
## Features
@ -43,19 +44,19 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
* RaspiBlitz over [LAN or Tor](prepare_remote_node.md#raspiblitz)
* RoninDojo over [LAN or Tor](prepare_remote_node.md#ronindojo)
* Start a pruned node from https://pruned.host4coins.net/blocks
* JoininBox is part of the RaspiBlitz SERVICES
* JoininBox is part the RaspiBlitz SERVICES
**The addresses, transactions and balances of JoinMarket can be seen in the watch-only-descriptor-wallet of the connected node.**
**The addresses, transactions and balances of JoinMarket can be seen in the watch-only wallet of the connected node.**
* use your own or a trusted node
* to protect privacy in case of physical access use disk encryption
## Required Hardware
### A computer running a Debian / Ubuntu Linux flavour.
* See the [tested-environments](#tested-environments).
### RaspberryPi 5 or 4
### RaspberryPi 4 or 3
* Power supply (5V 3A and above recommended)
* Heatsink case
* 32 GB Endurance type SDcard
* 32 GB Endurence type SDcard
* [(USB SSD to run a pruned bitcoin node locally)](FAQ.md#usb-ssd-recommendation)
### VPS eg: [host4coins.net](https://host4coins.net/)
Recommended minimum:
@ -161,3 +162,8 @@ the default password is: `joininbox` - will be prompted to change it on the firs
* IRC: #joinmarket on [libera.chat](https://libera.chat/) or [hackint.org](https://hackint.org/)
* Reddit: <https://www.reddit.com/r/joinmarket/>
* Keybase: <https://keybase.io/team/raspiblitz#joinmarket>
## Donations
* For JoinMarket (general): https://bitcoinprivacy.me/joinmarket-donations
* To waxwing for JoinMarket: <https://joinmarket.me/donations/>
* To openoms for JoininBox (LN + payjoin enabled - open in the [Tor Browser](https://www.torproject.org/)): <http://7tpv3ynajkv6cdocmzitcd4z3xrstp3ic6xtv5om3dc2ned3fffll5qd.onion/apps/4FePMm7m818oppkTYNZRwbDnL6HP/pos>

View file

@ -276,30 +276,22 @@ echo "##########"
echo
# apt dependencies for python
apt-get install -y python3 virtualenv python3-venv python3-dev python3-wheel python3-jinja2 python3-pip
if [ -f "/usr/bin/python3.13" ]; then
# use python 3.13 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.13 1
echo "# python calls python3.13"
elif [ -f "/usr/bin/python3.12" ]; then
# use python 3.12 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.12 1
echo "# python calls python3.12"
elif [ -f "/usr/bin/python3.11" ]; then
# use python 3.11 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1
echo "# python calls python3.11"
elif [ -f "/usr/bin/python3.10" ]; then
# use python 3.10 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1
echo "# python calls python3.10"
if [ -f "/usr/bin/python3.8" ]; then
# use python 3.8 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1
echo "# python calls python3.8"
elif [ -f "/usr/bin/python3.9" ]; then
# use python 3.9 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1
echo "# python calls python3.9"
elif [ -f "/usr/bin/python3.8" ]; then
# use python 3.8 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1
echo "# python calls python3.8"
elif [ -f "/usr/bin/python3.10" ]; then
# use python 3.10 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1
echo "# python calls python3.10"
elif [ -f "/usr/bin/python3.11" ]; then
# use python 3.11 if available
update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1
echo "# python calls python3.11"
else
echo "# FAIL- there is no tested version of python present"
exit 1
@ -399,11 +391,11 @@ else
exit 1
fi
command="bash /home/joinmarket/joininbox/scripts/verify.git.sh \
command="sudo -u joinmarket bash /home/joinmarket/joininbox/scripts/verify.git.sh \
${PGPsigner} ${PGPpubkeyLink} ${PGPpubkeyFingerprint}"
echo "running: ${command}"
chmod 777 /dev/shm
sudo -u joinmarket ${command} || exit 1
${command} || exit 1
runuser joinmarket -c "cp /home/joinmarket/joininbox/scripts/* /home/joinmarket/"
runuser joinmarket -c "cp /home/joinmarket/joininbox/scripts/.* /home/joinmarket/ 2>/dev/null"

View file

@ -1,120 +0,0 @@
# images, checksums and signatures are at:
# https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/
# NOTE: This template is intended to be invoked via the wrapper script:
# ci/amd64/packer.build.amd64-debian.sh
# The wrapper resolves and injects the latest point-release ISO name and matching checksum
# at runtime. Defaults below are placeholders and are not guaranteed to work if you run
# `packer build` directly.
variable "iso_name" { default = "debian-13-amd64-netinst.iso" }
variable "iso_checksum" { default = "file:https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA256SUMS" }
variable "github_user" { default = "openoms" }
variable "branch" { default = "master" }
variable "boot" { default = "uefi" }
variable "preseed_file" { default = "preseed.cfg" }
variable "hostname" { default = "joininbox-amd64" }
variable "image_size" { default = "30000" }
variable "image_type" { default = "qcow2" }
variable "memory" { default = "2048" }
variable "cpus" { default = "2" }
locals {
name_template = "joininbox-amd64-debian"
image_extension = var.image_type == "raw" ? "img" : var.image_type
bios_file = var.boot == "uefi" ? "OVMF.fd" : "bios-256k.bin"
boot_command = var.boot == "uefi" ? [
"<wait><wait><wait>c<wait><wait><wait>",
"linux /install.amd/vmlinuz ",
"auto=true ",
"url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/${var.preseed_file} ",
"hostname=${var.hostname} ",
"domain=${var.hostname}.local ",
"interface=auto ",
"vga=788 noprompt quiet --<enter>",
"initrd /install.amd/initrd.gz<enter>",
"boot<enter>"
] : [
"<esc><wait>install <wait>",
"<wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait><wait> preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/${var.preseed_file} <wait>",
"debian-installer=en_US.UTF-8 <wait>",
"auto <wait>",
"locale=en_US.UTF-8 <wait>",
"kbd-chooser/method=us <wait>",
"keyboard-configuration/xkb-keymap=us <wait>",
"netcfg/get_hostname=${var.hostname} <wait>",
"netcfg/get_domain=${var.hostname}.local <wait>",
"fb=false <wait>",
"debconf/frontend=noninteractive <wait>",
"console-setup/ask_detect=false <wait>",
"console-keymaps-at/keymap=us <wait>",
"grub-installer/bootdev=default <wait>",
"<enter><wait>"
]
}
source "qemu" "debian" {
boot_command = local.boot_command
boot_wait = "5s"
cpus = var.cpus
disk_size = var.image_size
http_directory = "./http"
iso_checksum = var.iso_checksum
iso_url = "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/${var.iso_name}"
memory = var.memory
output_directory = "../builds/${local.name_template}-qemu"
shutdown_command = "echo 'joininbox' | sudo /sbin/shutdown -hP now"
ssh_password = "joininbox"
ssh_port = 22
ssh_timeout = "10000s"
ssh_username = "joinmarket"
format = var.image_type
vm_name = "${local.name_template}.${local.image_extension}"
headless = false
vnc_bind_address = "127.0.0.1"
vnc_port_max = 5900
vnc_port_min = 5900
qemuargs = [
["-m", var.memory],
["-bios", local.bios_file],
["-display", "none"]
]
}
build {
description = "JoininBox amd64 Debian image build"
sources = ["source.qemu.debian"]
provisioner "shell" {
environment_vars = [
"HOME_DIR=/home/joinmarket",
"github_user=${var.github_user}",
"branch=${var.branch}"
]
execute_command = "echo 'joininbox' | {{.Vars}} sudo -S -E sh -eux '{{.Path}}'"
expect_disconnect = true
scripts = [
"./scripts/update.sh",
"./../_common/sshd.sh",
"./scripts/networking.sh",
"./scripts/sudoers.sh",
"./scripts/systemd.sh",
"./scripts/joininbox.sh",
"./scripts/cleanup.sh"
]
}
}
packer {
required_version = ">= 1.7.0, < 2.0.0"
required_plugins {
qemu = {
source = "github.com/hashicorp/qemu"
version = ">= 1.0.0, < 2.0.0"
}
}
}

View file

@ -0,0 +1,47 @@
choose-mirror-bin mirror/http/proxy string
d-i apt-setup/use_mirror boolean true
d-i base-installer/kernel/override-image string linux-server
d-i clock-setup/utc boolean true
d-i clock-setup/utc-auto boolean true
d-i finish-install/reboot_in_progress note
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i keymap select us
d-i mirror/country string manual
d-i mirror/http/directory string /debian
d-i mirror/http/hostname string ftp.de.debian.org
d-i mirror/http/proxy string
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true
d-i passwd/root-login boolean false
d-i passwd/root-password-again password joininbox
d-i passwd/root-password password joininbox
d-i passwd/user-fullname string joinmarket
d-i passwd/user-uid string 1000
d-i passwd/user-password password joininbox
d-i passwd/user-password-again password joininbox
d-i passwd/username string joinmarket
d-i pkgsel/include string sudo bzip2 acpid cryptsetup zlib1g-dev wget curl dkms fuse make nfs-common net-tools cifs-utils rsync
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select full-upgrade
# Prevent packaged version of VirtualBox Guest Additions being installed:
d-i preseed/early_command string sed -i \
'/in-target/idiscover(){/sbin/discover|grep -v VirtualBox;}' \
/usr/lib/pre-pkgsel.d/20install-hwpackages
d-i time/zone string UTC
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false
d-i preseed/late_command string sed -i '/^deb cdrom:/s/^/#/' /target/etc/apt/sources.list
apt-cdrom-setup apt-setup/cdrom/set-first boolean false
apt-mirror-setup apt-setup/use_mirror boolean true
popularity-contest popularity-contest/participate boolean false
tasksel tasksel/first multiselect standard, ssh-server

View file

@ -1,72 +0,0 @@
# https://github.com/chef/bento/blob/main/packer_templates/http/debian/preseed.cfg
# https://www.debian.org/releases/stable/example-preseed.txt
# https://github.com/tylert/packer-build/blob/master/source/debian/12_bookworm/base-uefi.preseed
# variables: https://github.com/tylert/packer-build/blob/master/source/debian/12_bookworm/base-uefi.pkr.hcl
# Locale Setup
d-i debian-installer/language string en
d-i debian-installer/country string US
d-i debian-installer/locale string en_US.UTF-8
# d-i localechooser/supported-locales multiselect en_CA.UTF-8 fr_CA.UTF-8 zh_CN.UTF-8
# d-i pkgsel/install-language-support boolean true
# Keyboard Setup
d-i keyboard-configuration/xkb-keymap select us
# Clock Setup
# d-i time/zone string Canada/Eastern
d-i time/zone string UTC
d-i clock-setup/utc boolean true
# set above to false if making a bootable USB to run on same system as Windows
# Network Setup
d-i netcfg/get_hostname string joininbox-amd64
d-i netcfg/get_domain string
# https://bugs.launchpad.net/ubuntu/+source/netcfg/+bug/713385
d-i netcfg/choose_interface select auto
# make sure you also add "interface=auto" to your boot command too
# https://bugs.launchpad.net/ubuntu/+source/netcfg/+bug/713385
# User Setup
d-i passwd/root-login boolean false
d-i passwd/root-password-again password joininbox
d-i passwd/root-password password joininbox
d-i passwd/user-fullname string joinmarket
d-i passwd/user-uid string 1000
d-i passwd/user-password password joininbox
d-i passwd/user-password-again password joininbox
d-i passwd/username string joinmarket
# Package Setup
d-i hw-detect/load_firmware boolean false
d-i hw-detect/load_media boolean false
apt-cdrom-setup apt-setup/cdrom/set-first boolean false
d-i mirror/country string manual
d-i mirror/http/hostname string httpredir.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i apt-setup/contrib boolean true
d-i apt-setup/non-free boolean true
tasksel tasksel/first multiselect ssh-server, standard
d-i pkgsel/include string sudo bzip2 acpid cryptsetup zlib1g-dev wget curl dkms fuse make nfs-common net-tools cifs-utils rsync
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false
# Drive setup
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true
# Final Setup
d-i finish-install/reboot_in_progress note

View file

@ -0,0 +1,70 @@
{
"variables": {
"iso_name": "debian-12.11.0-amd64-netinst.iso",
"iso_checksum": "30ca12a15cae6a1033e03ad59eb7f66a6d5a258dcf27acd115c2bd42d22640e8",
"box_basename": "debian",
"build_directory": "../builds",
"headless": "false",
"http_directory": "{{template_dir}}/http",
"name": "debian",
"preseed_path": "debian-9/preseed.cfg",
"qemu_bios": "bios-256k.bin",
"template": "joininbox-amd64-debian",
"boot_command": "<esc><wait>install <wait> preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/{{user `preseed_path`}} <wait>debian-installer=en_US.UTF-8 <wait>auto <wait>locale=en_US.UTF-8 <wait>kbd-chooser/method=us <wait>keyboard-configuration/xkb-keymap=us <wait>netcfg/get_hostname={{ .Name }} <wait>netcfg/get_domain=vagrantup.com <wait>fb=false <wait>debconf/frontend=noninteractive <wait>console-setup/ask_detect=false <wait>console-keymaps-at/keymap=us <wait>grub-installer/bootdev=default <wait><enter><wait>",
"memory": "2048"
},
"builders": [
{
"boot_command": "{{user `boot_command`}}",
"boot_wait": "5s",
"cpus": "2",
"disk_size": "30000",
"headless": "{{ user `headless` }}",
"http_directory": "{{user `http_directory`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_url": "http://cdimage.debian.org/cdimage/release/current/amd64/iso-cd/{{user `iso_name`}}",
"memory": "{{ user `memory` }}",
"output_directory": "{{ user `build_directory` }}/{{user `template`}}-qemu",
"shutdown_command": "echo 'joininbox' | sudo /sbin/shutdown -hP now",
"ssh_password": "joininbox",
"ssh_port": "22",
"ssh_timeout": "10000s",
"ssh_username": "joinmarket",
"type": "qemu",
"format": "qcow2",
"vm_name": "{{ user `template` }}.qcow2",
"vnc_bind_address": "127.0.0.1",
"vnc_port_max": "5900",
"vnc_port_min": "5900",
"qemuargs": [
[ "-m", "{{ user `memory` }}" ],
[ "-bios", "{{ user `qemu_bios` }}" ],
[ "-display", "none" ]
]
}
],
"provisioners": [
{
"type": "shell",
"environment_vars": [
"HOME_DIR=/home/joinmarket",
"http_proxy={{user `http_proxy`}}",
"https_proxy={{user `https_proxy`}}",
"no_proxy={{user `no_proxy`}}",
"github_user={{user `github_user`}}",
"branch={{user `branch`}}"
],
"execute_command": "echo 'joininbox' | {{.Vars}} sudo -S -E sh -eux '{{.Path}}'",
"expect_disconnect": true,
"scripts": [
"{{template_dir}}/scripts/update.sh",
"{{template_dir}}/../_common/sshd.sh",
"{{template_dir}}/scripts/networking.sh",
"{{template_dir}}/scripts/sudoers.sh",
"{{template_dir}}/scripts/systemd.sh",
"{{template_dir}}/scripts/joininbox.sh",
"{{template_dir}}/scripts/cleanup.sh"
]
}
]
}

View file

@ -1,135 +1,39 @@
#!/bin/bash -e
sudo apt-get update
# install packer
if ! packer version 2>/dev/null; then
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository -y "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update
echo -e "\nInstalling packer..."
sudo apt-get install -y packer
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository -y "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update
echo -e "\nInstalling packer..."
sudo apt-get install -y packer
else
echo "# Packer is installed"
echo "# Packer is installed"
fi
# install qemu and UEFI firmware
# install qemu
echo "# Install qemu ..."
sudo apt-get update
sudo apt-get install -y qemu-system ovmf
sudo apt-get install -y qemu-system
# install qemu plugin
packer plugins install github.com/hashicorp/qemu
# set vars from positional arguments (for backward compatibility with CI)
if [ $# -gt 0 ]; then
github_user=$1
github_user=$1
else
github_user=openoms
github_user=openoms
fi
if [ $# -gt 1 ]; then
branch=$2
branch=$2
else
branch=master
branch=master
fi
# Resolve latest Debian 13 amd64 netinst ISO from SHA256SUMS.
# This avoids 404s and checksum mismatches when Debian point releases rotate.
debian_major=${DEBIAN_MAJOR:-13}
debian_iso_dir="https://cdimage.debian.org/debian-cd/current/amd64/iso-cd"
debian_sums_url="${debian_iso_dir}/SHA256SUMS"
debian_sums_sig_url="${debian_iso_dir}/SHA256SUMS.sign"
debian_cd_key_urls=(
"https://www.debian.org/CD/key-DA87E80D6294BE9B.txt"
"https://www.debian.org/CD/key-988021A964E6EA7D.txt"
)
debian_cd_expected_fprs=(
"DF9B9C49EAA9298432589D76DA87E80D6294BE9B"
"10460DAD76165AD81FBC0CE9988021A964E6EA7D"
)
if ! command -v gpgv >/dev/null 2>&1; then
echo "# Installing gpgv"
sudo apt-get install -y gpgv
fi
if ! command -v gpgv >/dev/null 2>&1; then
echo "ERROR: gpgv is required for signature verification"
exit 1
fi
tmp_checksums_dir=$(mktemp -d)
trap 'rm -rf "${tmp_checksums_dir}"' EXIT
echo "# Downloading checksum files"
curl -fsSL "${debian_sums_url}" -o "${tmp_checksums_dir}/SHA256SUMS"
curl -fsSL "${debian_sums_sig_url}" -o "${tmp_checksums_dir}/SHA256SUMS.sign"
echo "# Verifying SHA256SUMS signature (PGP)"
cd_keyring="${tmp_checksums_dir}/debian-cd-signing-keys.gpg"
tmp_gnupg_home="${tmp_checksums_dir}/gnupg-home"
mkdir -p "${tmp_gnupg_home}"
chmod 700 "${tmp_gnupg_home}"
for i in "${!debian_cd_key_urls[@]}"; do
key_url="${debian_cd_key_urls[$i]}"
expected_fpr="${debian_cd_expected_fprs[$i]}"
key_file="${tmp_checksums_dir}/cd-key-${i}.asc"
curl -fsSL "${key_url}" -o "${key_file}"
actual_fpr=$(gpg --homedir "${tmp_gnupg_home}" --show-keys --with-colons "${key_file}" 2>/dev/null | awk -F: '/^fpr:/ {print $10; exit}')
if [ -z "${actual_fpr}" ] || [ "${actual_fpr}" != "${expected_fpr}" ]; then
echo "# SHA256SUMS signature: FAIL"
echo "ERROR: Unexpected fingerprint for ${key_url}"
exit 1
fi
gpg --homedir "${tmp_gnupg_home}" --no-default-keyring --keyring "${cd_keyring}" --import "${key_file}" >/dev/null 2>&1
done
if gpgv --keyring "${cd_keyring}" "${tmp_checksums_dir}/SHA256SUMS.sign" "${tmp_checksums_dir}/SHA256SUMS" >/dev/null 2>&1; then
echo "# SHA256SUMS signature: PASS"
else
echo "# SHA256SUMS signature: FAIL"
echo "ERROR: PGP signature verification failed for ${debian_sums_url}"
exit 1
fi
echo "# Resolving latest Debian ${debian_major} amd64 netinst ISO from ${debian_sums_url}"
latest_iso_line=$(awk -v major="${debian_major}" '$2 ~ ("^\\*?\\.?/?debian-" major "\\.[0-9]+\\.[0-9]+-amd64-netinst\\.iso$") {print $1 " " $2}' "${tmp_checksums_dir}/SHA256SUMS" | \
sort -k2 -V | tail -1)
if [ -z "${latest_iso_line}" ]; then
echo "ERROR: Could not resolve latest Debian ${debian_major} amd64 netinst ISO from ${debian_sums_url}"
exit 1
fi
latest_iso_checksum=$(echo "${latest_iso_line}" | awk '{print $1}')
latest_iso_name=$(echo "${latest_iso_line}" | awk '{print $2}' | sed 's#^\*##; s#^\./##; s#^/##')
if [ -z "${latest_iso_name}" ] || [ -z "${latest_iso_checksum}" ]; then
echo "ERROR: Failed parsing ISO name/checksum from: ${latest_iso_line}"
exit 1
fi
resolved_checksum=$(awk -v iso="${latest_iso_name}" '($2 == iso || $2 == "*" iso || $2 == "./" iso || $2 == "/" iso) {print $1; exit}' "${tmp_checksums_dir}/SHA256SUMS")
if [ -z "${resolved_checksum}" ]; then
echo "ERROR: Could not find checksum entry for ${latest_iso_name} in ${debian_sums_url}"
exit 1
fi
echo "# Debian ISO selection"
echo "# ISO filename : ${latest_iso_name}"
echo "# SHA256 (selected) : ${latest_iso_checksum}"
echo "# SHA256 (resolved) : ${resolved_checksum}"
if [ "${latest_iso_checksum}" = "${resolved_checksum}" ]; then
echo "# Checksum verify : PASS"
else
echo "# Checksum verify : FAIL"
echo "ERROR: Checksum mismatch for ${latest_iso_name}"
exit 1
fi
vars="-var github_user=${github_user} -var branch=${branch} -var iso_name=${latest_iso_name} -var iso_checksum=${latest_iso_checksum}"
# Build the image
echo "# Build the image with: github_user=${github_user} branch=${branch}"
echo "# Building image ..."
cd debian
packer init -upgrade .
PACKER_LOG=1 packer build ${vars} -only=qemu.debian build.amd64-debian.pkr.hcl || exit 1
PACKER_LOG=1 packer build \
-var github_user=${github_user} -var branch=${branch} \
-only=qemu joininbox-amd64-debian.json

View file

@ -1,138 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
image="${1:-${GITHUB_WORKSPACE:-$(pwd)}/ci/amd64/builds/joininbox-amd64-debian-qemu/joininbox-amd64-debian.qcow2}"
ssh_port="${SSH_PORT:-2222}"
ssh_password="${SSH_PASSWORD:-joininbox}"
qemu_pid_file="${RUNNER_TEMP:-/tmp}/joininbox-qemu.pid"
bats_core_dir="${BATS_CORE_DIR:-${GITHUB_WORKSPACE:-$(pwd)}/.bats-core}"
if [ ! -f "${image}" ]; then
echo "Missing image: ${image}" >&2
exit 1
fi
if [ ! -x "${bats_core_dir}/bin/bats" ]; then
echo "Missing bats-core checkout: ${bats_core_dir}" >&2
exit 1
fi
sudo apt-get update
sudo apt-get install -y ovmf qemu-system-x86 sshpass
ssh_opts=(
-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
-o ConnectTimeout=5
-p "${ssh_port}"
)
ovmf_code="${OVMF_CODE:-${OVMF_BIOS:-}}"
ovmf_vars_template="${OVMF_VARS:-}"
ovmf_vars="${RUNNER_TEMP:-/tmp}/joininbox-ovmf-vars.fd"
qemu_firmware_args=()
if [ -z "${ovmf_code}" ]; then
for candidate in \
/usr/share/OVMF/OVMF_CODE_4M.fd \
/usr/share/OVMF/OVMF_CODE_4M.secboot.fd \
/usr/share/OVMF/OVMF_CODE_4M.ms.fd \
/usr/share/OVMF/OVMF_CODE.fd \
/usr/share/OVMF/OVMF.fd \
/usr/share/ovmf/OVMF_CODE_4M.fd \
/usr/share/ovmf/OVMF_CODE_4M.secboot.fd \
/usr/share/ovmf/OVMF_CODE_4M.ms.fd \
/usr/share/ovmf/OVMF_CODE.fd \
/usr/share/ovmf/OVMF.fd \
OVMF.fd; do
if [ -f "${candidate}" ]; then
ovmf_code="${candidate}"
break
fi
done
fi
if [ -z "${ovmf_code}" ] || [ ! -f "${ovmf_code}" ]; then
echo "No OVMF firmware found. Set OVMF_CODE or OVMF_BIOS to the firmware path." >&2
find /usr/share/OVMF /usr/share/ovmf -maxdepth 1 -type f -name '*.fd' -print 2>/dev/null || true
exit 1
fi
case "${ovmf_code##*/}" in
*CODE*)
if [ -z "${ovmf_vars_template}" ]; then
for candidate in \
"${ovmf_code/CODE/VARS}" \
/usr/share/OVMF/OVMF_VARS_4M.fd \
/usr/share/OVMF/OVMF_VARS.fd \
/usr/share/ovmf/OVMF_VARS_4M.fd \
/usr/share/ovmf/OVMF_VARS.fd; do
if [ -f "${candidate}" ]; then
ovmf_vars_template="${candidate}"
break
fi
done
fi
if [ -z "${ovmf_vars_template}" ] || [ ! -f "${ovmf_vars_template}" ]; then
echo "No OVMF VARS template found for ${ovmf_code}. Set OVMF_VARS to the template path." >&2
find /usr/share/OVMF /usr/share/ovmf -maxdepth 1 -type f -name '*.fd' -print 2>/dev/null || true
exit 1
fi
cp "${ovmf_vars_template}" "${ovmf_vars}"
qemu_firmware_args=(
-drive "if=pflash,format=raw,readonly=on,file=${ovmf_code}"
-drive "if=pflash,format=raw,file=${ovmf_vars}"
)
;;
*)
qemu_firmware_args=(-bios "${ovmf_code}")
;;
esac
cleanup() {
if [ -f "${qemu_pid_file}" ]; then
qemu_pid="$(cat "${qemu_pid_file}")"
if kill -0 "${qemu_pid}" 2>/dev/null; then
kill "${qemu_pid}" 2>/dev/null || true
timeout 30s tail --pid="${qemu_pid}" -f /dev/null 2>/dev/null ||
kill -9 "${qemu_pid}" 2>/dev/null ||
true
fi
fi
}
trap cleanup EXIT
rm -f "${qemu_pid_file}"
qemu-system-x86_64 \
-m 2048 \
-smp 2 \
"${qemu_firmware_args[@]}" \
-drive "file=${image},format=qcow2" \
-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:${ssh_port}-:22" \
-device e1000,netdev=net0 \
-display none \
-snapshot \
-pidfile "${qemu_pid_file}" \
-daemonize
echo "Waiting for SSH in the booted image"
for attempt in {1..120}; do
if sshpass -p "${ssh_password}" ssh "${ssh_opts[@]}" joinmarket@127.0.0.1 "true" 2>/dev/null; then
break
fi
if [ "${attempt}" -eq 120 ]; then
echo "Timed out waiting for SSH" >&2
exit 1
fi
sleep 5
done
tar -C "${bats_core_dir}" -cf - . |
sshpass -p "${ssh_password}" ssh "${ssh_opts[@]}" joinmarket@127.0.0.1 \
"mkdir -p /tmp/bats-core && tar -C /tmp/bats-core -xf -"
sshpass -p "${ssh_password}" ssh "${ssh_opts[@]}" joinmarket@127.0.0.1 \
"PATH=/tmp/bats-core/bin:\$PATH /home/joinmarket/joininbox/test/run-bats-local.sh"

View file

@ -45,7 +45,7 @@ This can be skipped if you [connect through Tor](#tor-connection)
Add the values:
* `rpcallowip=JOININBOX_IP` or `RANGE`
* either specify the LAN IP of the computer (here JoininBox)
* or use a range like: `192.168.1.0/24` - edit to your local subnet - the first 3 numbers of the LAN IP address, the example used here is: 192.168.1.x
* or use a range like: `192.168.1.0/24` - edit to your local subnet - the first 3 numbes of the LAN IP address, the example used here is: 192.168.1.x
* `rpcbind=LAN_IP_OF_THE_NODE`
* use the local IP of the bitcoin node in the example: `192.168.1.4`
* can keep the other `rpcallowip` and `rpcbind` entries especially for the localhost: `127.0.0.1`

View file

@ -42,7 +42,7 @@ dialog_color = (CYAN,BLACK,OFF)
title_color = (CYAN,BLACK,ON)
# Dialog box border color
border_color = (CYAN,BLACK,ON)
border_color = (BLACK,BLACK,ON)
# Active button color
button_active_color = (BLACK,CYAN,ON)

View file

@ -9,7 +9,7 @@ joininConfPath="/home/joinmarket/joinin.conf"
function downloadBitcoinCore() {
# set version
# https://bitcoincore.org/en/download/
bitcoinVersion="29.2"
bitcoinVersion="26.1"
if bitcoin-cli --version | grep $bitcoinVersion >/dev/null; then
echo "# Bitcoin Core $bitcoinVersion is already installed"
@ -247,8 +247,8 @@ setJMconfigToSignet() {
sed -i "s/^rpc_password =.*/rpc_password = $RPCPWSIGNET/g" $JMcfgPath
echo "# rpc_password = $RPCPWSIGNET"
# rpc_wallet_file
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = watch-only-descriptor-wallet/g" $JMcfgPath
echo "# using the bitcoind watch-only-descriptor-wallet"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = wallet.dat/g" $JMcfgPath
echo "# using the bitcoind wallet: wallet.dat"
# rpc_host
sed -i "s/^rpc_host =.*/rpc_host = 127.0.0.1/g" $JMcfgPath
echo "# rpc_host = 127.0.0.1"
@ -321,79 +321,38 @@ function getRPC {
fi
}
# getConnectedBitcoinCoreVersion - read the numeric version over node RPC
function getConnectedBitcoinCoreVersion {
local tor=""
if [ "$(echo "$rpc_host" | grep -c .onion)" -gt 0 ]; then
tor="torsocks"
fi
$tor curl -sS --data-binary \
'{"jsonrpc": "1.0", "id":"get_bitcoin_core_version", "method": "getnetworkinfo", "params": []}' \
"http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/" 2>/dev/null |
jq -r '.result.version // empty' 2>/dev/null
}
# migrateLegacyRPCWalletConfig - switch the persisted JoinMarket RPC wallet
# from the legacy wallet.dat name to the descriptor wallet used by JoininBox
# when the connected Bitcoin Core version is v30.0 or newer
function migrateLegacyRPCWalletConfig {
if [ "$rpc_wallet" != "wallet.dat" ]; then
return 0
fi
local bitcoinCoreVersion
bitcoinCoreVersion=$(getConnectedBitcoinCoreVersion)
if ! [[ "$bitcoinCoreVersion" =~ ^[0-9]+$ ]]; then
echo "# Could not determine the connected Bitcoin Core version; keeping wallet.dat"
return 0
fi
# Bitcoin Core's numeric version is 290200 for v29.2 and 300000 for v30.0.
if [ "$bitcoinCoreVersion" -lt 300000 ]; then
echo "# Connected Bitcoin Core is v29.x or earlier; keeping wallet.dat"
return 0
fi
echo "# Migrating the configured Bitcoin Core wallet from wallet.dat to watch-only-descriptor-wallet"
local migrationConfigOutput
if ! migrationConfigOutput=$(mktemp "${JMcfgPath}.XXXXXX"); then
echo "# Failed to create a temporary descriptor wallet configuration" >&2
return 1
fi
if ! sed \
"s/^rpc_wallet_file =.*/rpc_wallet_file = watch-only-descriptor-wallet/g" \
"$JMcfgPath" >"$migrationConfigOutput"; then
rm -f "$migrationConfigOutput"
echo "# Failed to prepare the descriptor wallet configuration" >&2
return 1
fi
if ! mv "$migrationConfigOutput" "$JMcfgPath"; then
rm -f "$migrationConfigOutput"
echo "# Failed to update the descriptor wallet configuration" >&2
return 1
fi
getRPC
if [ "$rpc_wallet" != "watch-only-descriptor-wallet" ]; then
echo "# Failed to select the descriptor wallet configuration" >&2
return 1
fi
}
# checkRPCwallet <wallet>
function checkRPCwallet {
getRPC
migrateLegacyRPCWalletConfig || return 1
if [ $# -eq 0 ]; then
rpc_wallet=$rpc_wallet
else
rpc_wallet=$1
fi
echo "# Check 'deprecatedrpc=create_bdb' in bitcoin.conf"
source ${joininConfPath}
if [ $runningEnv = standalone ]; then
bitcoinConfPath="/home/bitcoin/.bitcoin/bitcoin.conf"
elif [ $runningEnv = raspiblitz ]; then
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
bitcoinConfPath="/mnt/hdd/bitcoin/bitcoin.conf"
else
bitcoinConfPath="/mnt/hdd/app-data/bitcoin/bitcoin.conf"
fi
fi
if ! sudo grep -c "deprecatedrpc=create_bdb" "$bitcoinConfPath"; then
echo "# Place 'deprecatedrpc=create_bdb' in bitcoin.conf"
echo "deprecatedrpc=create_bdb" | sudo tee -a "$bitcoinConfPath"
echo "# Restarting bitcoind"
sudo systemctl restart bitcoind
fi
echo "# Making sure the set $rpc_wallet wallet is present in bitcoind"
trap 'rm -f "$connectionOutput"' EXIT
connectionOutput=$(mktemp -p /dev/shm/)
walletFound=$(customRPC "# Check wallet" "listwallets" 2>$connectionOutput | grep -c "$rpc_wallet")
if [ $walletFound -eq 0 ]; then
echo "# Setting a watch-only-descriptor-wallet in Bitcoin Core named $rpc_wallet"
echo "# Setting a watch only wallet in Bitcoin Core named $rpc_wallet"
tor=""
if [ $(echo $rpc_host | grep -c .onion) -gt 0 ]; then
tor="torsocks"
@ -402,8 +361,8 @@ function checkRPCwallet {
fi
#TODO rewrite customRPC to support multiple params
$tor curl -sS --data-binary \
'{"jsonrpc": "1.0", "id":"# Create the bitcoind wallet", "method": "createwallet", "params": {"wallet_name":"'"$rpc_wallet"'","descriptors":true,"disable_private_keys":true}}' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/ | jq .
'{"jsonrpc": "1.0", "id":"# Create the bitcoind wallet", "method": "createwallet", "params": {"wallet_name":"'"$rpc_wallet"'","descriptors":false}}' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
echo
walletFound=$(customRPC "# Check wallet" "listwallets" 2>$connectionOutput | grep -c "$rpc_wallet")
if [ $walletFound -eq 0 ]; then
@ -415,75 +374,6 @@ function checkRPCwallet {
echo
fi
echo "# The wallet: $rpc_wallet is present and loaded in the connected bitcoind"
# Check for wallet migration from legacy wallet.dat
checkWalletMigration
}
# checkWalletMigration - detects legacy wallet.dat and guides user through migration
# This function checks if:
# 1. The old wallet.dat exists in Bitcoin Core
# 2. The new watch-only-descriptor-wallet is being used
# 3. Migration has not been completed yet
# If migration is needed, it prompts the user to rescan after opening their JM wallets
function checkWalletMigration {
# Skip if migration was already completed
if grep -q "walletMigrationDone=true" "${joininConfPath}" 2>/dev/null; then
return 0
fi
# Skip if we're not using the new descriptor wallet
if [ "$rpc_wallet" != "watch-only-descriptor-wallet" ]; then
return 0
fi
# RPC settings are already available from parent checkRPCwallet function
tor=""
if [ "$(echo "$rpc_host" | grep -c .onion)" -gt 0 ]; then
tor="torsocks"
fi
# Check if old wallet.dat exists in bitcoind (try to load it to see if it exists)
# First check listwalletdir for wallet.dat
oldWalletExists=$($tor curl -sS --data-binary \
'{"jsonrpc": "1.0", "id":"check_old_wallet", "method": "listwalletdir", "params": []}' \
"http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/" 2>/dev/null | jq -r '.result.wallets[].name' 2>/dev/null | grep -c "^wallet.dat$")
if [ "$oldWalletExists" -gt 0 ]; then
echo
echo "########################################################################"
echo "# WALLET MIGRATION NOTICE"
echo "########################################################################"
echo
echo "# A legacy wallet.dat was detected in Bitcoin Core."
echo "# JoininBox now uses descriptor wallets (watch-only-descriptor-wallet)"
echo "# for better compatibility with modern Bitcoin Core versions."
echo
echo "# To complete the migration and see your transaction history:"
echo
echo "# 1. Open each of your JoinMarket wallets once using:"
echo "# WALLET -> DISPLAY"
echo "# This imports the addresses into the new descriptor wallet."
echo
echo "# 2. After opening all wallets, run a blockchain rescan:"
echo "# WALLET -> RESCAN"
echo "# Use blockheight 481824 (first SegWit block) or later if you know when your wallet had its first deposit."
echo
echo "# The rescan may take several hours depending on wallet age."
echo "# You can monitor progress in the Bitcoin Core debug.log"
echo
echo "########################################################################"
echo
echo "# Press ENTER to continue..."
read -r
# Mark migration notice as shown (user can still run rescan manually)
if ! grep -q "walletMigrationDone=" "${joininConfPath}" 2>/dev/null; then
echo "walletMigrationDone=true" >>"${joininConfPath}"
else
sed -i "s/^walletMigrationDone=.*/walletMigrationDone=true/g" "${joininConfPath}"
fi
fi
}
# customRPC - sends a custom RPC command
@ -545,7 +435,7 @@ function connectLocalNode() {
elif [ "${network}" = testnet ]; then
rpc_port="18332"
fi
rpc_wallet="watch-only-descriptor-wallet"
rpc_wallet="wallet.dat"
if [ $runningEnv = raspiblitz ]; then
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
rpc_user=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)

View file

@ -14,14 +14,6 @@ walletPath="/home/joinmarket/.joinmarket/wallets/"
JMcfgPath="/home/joinmarket/.joinmarket/joinmarket.cfg"
joininConfPath="/home/joinmarket/joinin.conf"
## dialog fixes
# fallback for unknown terminal types (e.g., xterm-ghostty)
if ! infocmp "$TERM" &>/dev/null; then
export TERM=xterm-256color
fi
# fix dialog box drawing characters
export NCURSES_NO_UTF8_ACS=1
# functions
source /home/joinmarket/_functions.menu.sh
source /home/joinmarket/_functions.bitcoincore.sh
@ -170,16 +162,16 @@ function stopYG() {
function YGnickname() {
# Retrieves nickname from the latest NickServ message in the newest logfile
newest_log=$(ls -td /home/joinmarket/.joinmarket/logs/*J5* 2>/dev/null | head -n 1)
if [ -n "$newest_log" ] && [ -f "$newest_log" ]; then
name=$(grep NickServ "$newest_log" 2>/dev/null | tail -1 | awk '{print $9}')
if [ ${#name} -eq 0 ]; then
if ls -td /home/joinmarket/.joinmarket/logs/* 1>&2>/dev/null ; then
newest_log=$(ls -td /home/joinmarket/.joinmarket/logs/* | grep J5 | head -n 1)
name=$(grep NickServ $newest_log | tail -1 | awk '{print $9}')
if [ ${#name} -eq 0 ];then
name="no_Nick_see_LOGS"
fi
else
name="waiting__to__run"
fi
echo "$name"
echo $name
}
# copyJoininboxScripts
@ -282,8 +274,8 @@ function generateJMconfig() {
fi
sed -i "s/^rpc_port =.*/rpc_port = $RPCPORT/g" $JMcfgPath
echo "# rpc_port = $RPCPORT"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = watch-only-descriptor-wallet/g" $JMcfgPath
echo "# using the bitcoind watch-only-descriptor-wallet"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = wallet.dat/g" $JMcfgPath
echo "# using the bitcoind wallet: wallet.dat"
# set joinin.conf value
/home/joinmarket/set.value.sh set network mainnet ${joininConfPath}
fi

View file

@ -5,13 +5,13 @@ source /home/joinmarket/_functions.sh
# check connectedRemoteNode var in joinin.conf
if ! grep -Eq "^connectedRemoteNode=" $joininConfPath; then
echo "connectedRemoteNode=off" >>$joininConfPath
echo "connectedRemoteNode=off" >> $joininConfPath
fi
if [ "$1" = "signetOn" ]; then
installBitcoinCore
installSignet
if [ "$connectedRemoteNode" = "on" ]; then
if [ "$connectedRemoteNode" = "on" ];then
backupJMconf
fi
generateJMconfig
@ -24,16 +24,16 @@ if [ "$1" = "signetOn" ]; then
bitcoinUser="joinmarket"
cliPath="/home/joinmarket/bitcoin/"
fi
if [ ! -d /home/${bitcoinUser}/.bitcoin/signet/wallets/watch-only-descriptor-wallet ]; then
echo "# Create watch-only-descriptor-wallet for signet ..."
if [ ! -f /home/${bitcoinUser}/.bitcoin/signet/wallets/wallet.dat/wallet.dat ];then
echo "# Create wallet.dat for signet ..."
sleep 10
sudo -u ${bitcoinUser} ${cliPath}/bitcoin-cli -signet -named createwallet wallet_name=watch-only-descriptor-wallet descriptors=true disable_private_keys=true
sudo -u ${bitcoinUser} ${cliPath}/bitcoin-cli -signet -named createwallet wallet_name=wallet.dat descriptors=false
fi
elif [ "$1" = "signetOff" ]; then
removeSignetdService
isSignet=$(grep -c "network = signet" <$JMcfgPath)
if [ $isSignet -gt 0 ]; then
isSignet=$(grep -c "network = signet" < $JMcfgPath)
if [ $isSignet -gt 0 ];then
echo "# Removing the joinmarket.cfg with signet settings"
rm -f $JMcfgPath
else

View file

@ -1,21 +1,15 @@
#!/bin/bash
# https://github.com/JoinMarket-Org/joinmarket-clientserver/releases
# Use tag for verification when available, otherwise use commit hash
testedJMversion="v0.9.12"
testedJMversion="v0.9.11"
# https://github.com/JoinMarket-Org/joinmarket-clientserver/commits/master/
# Only used if testedJMversion is empty or not set
#testedJMcommit="ce32bafbb5d716bde61830f71266410249d43dbc"
PGPsigner="kristapsk"
PGPpkeys="https://github.com/kristapsk.gpg"
PGPcheck="33E472FE870C7E5D"
PGPsigner="AdamISZ"
PGPpkeys="https://github.com/AdamISZ.gpg"
# AdamISZ current primary code-signing key used for v0.9.12
PGPcheck="0F1C7345D9193D1C8E3F21810C44134F93234873"
#PGPsigner="kristapsk"
#PGPpkeys="https://github.com/kristapsk.gpg"
#PGPcheck="33E472FE870C7E5D"
#PGPsigner="waxwing"
#PGPpkeys="https://raw.githubusercontent.com/JoinMarket-Org/joinmarket-clientserver/master/pubkeys/AdamGibson.asc"
#PGPcheck="2B6FC204D9BF332D062B461A141001A1AF77F20B"
me="${0##/*}"
@ -27,7 +21,7 @@ usage() {
printf %s"${me} [--option <argument>]
a script to install, update or configure JoinMarket
the latest tested version: ${testedJMversion:-$testedJMcommit} is installed by default with the QT GUI
the latest tested version: $testedJMversion is installed by default with the QT GUI
Options:
-h, --help this help info
@ -116,12 +110,9 @@ done
: "${install:=install}"
range_argument install "install" "config" "update" "testPR" "commit"
# Use tag if set, otherwise use commit hash
: "${version:=${testedJMversion:-$testedJMcommit}}"
# Only check GitHub releases if version looks like a tag (starts with 'v')
if [[ "${version}" == v* ]]; then
curl -s "https://github.com/JoinMarket-Org/joinmarket-clientserver/releases/tag/${version}" | grep -q "\"message\": \"Version not found\"" && error_msg "'There is no: https://github.com/JoinMarket-Org/joinmarket-clientserver/releases/tag/${version}'"
fi
: "${version:=${testedJMversion}}"
curl -s "https://github.com/JoinMarket-Org/joinmarket-clientserver/release/tag/${version}" | grep -q "\"message\": \"Version not found\"" && error_msg "'There is no: https://github.com/JoinMarket-Org/joinmarket-clientserver/release/tag/${version}'"
: "${qtgui:=false}"
range_argument qtgui "0" "1" "false" "true"
@ -192,29 +183,37 @@ function installJoinMarket() {
elif [ "$install" = "update" ] && [ ${#2} -gt 0 ]; then
updateVersion="$2"
sudo -u ${user} git reset --hard $updateVersion
echo "# Verifying signature for version: $updateVersion"
# Determine if it's a tag (starts with 'v') or commit hash
if [[ "$updateVersion" == v* ]]; then
sudo -u ${user} bash /home/joinmarket/joininbox/scripts/verify.git.sh \
"${PGPsigner}" "${PGPpkeys}" "${PGPcheck}" "$updateVersion" || exit 1
else
sudo -u ${user} bash /home/joinmarket/joininbox/scripts/verify.git.sh \
"${PGPsigner}" "${PGPpkeys}" "${PGPcheck}" || exit 1
fi
else
# Decide whether to use tag or commit
if [ -n "${testedJMversion}" ]; then
echo "# Installing tested version: ${testedJMversion}"
sudo -u ${user} git reset --hard $testedJMversion
echo "# Verifying tag signature: ${testedJMversion}"
sudo -u ${user} bash /home/joinmarket/joininbox/scripts/verify.git.sh \
"${PGPsigner}" "${PGPpkeys}" "${PGPcheck}" "${testedJMversion}" || exit 1
sudo -u ${user} git reset --hard $testedJMversion
sudo -u ${user} wget --prefer-family=ipv4 -O "pgp_keys.asc" ${PGPpkeys}
sudo -u ${user} gpg --import --import-options show-only ./pgp_keys.asc
fingerprint=$(sudo -u ${user} gpg "pgp_keys.asc" 2>/dev/null | grep "${PGPcheck}" -c)
if [ ${fingerprint} -lt 1 ]; then
echo
echo "# WARNING --> the PGP fingerprint is not as expected for ${PGPsigner}"
echo "# Should contain PGP: ${PGPcheck}"
echo "# PRESS ENTER to TAKE THE RISK if you think all is OK"
read -r
fi
sudo -u ${user} gpg --import ./pgp_keys.asc
verifyResult=$(sudo -u ${user} git verify-tag $testedJMversion 2>&1)
goodSignature=$(echo ${verifyResult} | grep 'Good signature' -c)
echo "# goodSignature(${goodSignature})"
correctKey=$(echo ${verifyResult} | tr -d " \t\n\r" | grep "${PGPcheck}" -c)
echo "# correctKey(${correctKey})"
if [ ${correctKey} -lt 1 ] || [ ${goodSignature} -lt 1 ]; then
echo
echo "# BUILD FAILED --> PGP verification not OK / signature(${goodSignature}) verify(${correctKey})"
exit 1
else
echo "# Installing tested commit: ${testedJMcommit}"
sudo -u ${user} git reset --hard $testedJMcommit
echo "# Verifying commit signature: ${testedJMcommit}"
sudo -u ${user} bash /home/joinmarket/joininbox/scripts/verify.git.sh \
"${PGPsigner}" "${PGPpkeys}" "${PGPcheck}" || exit 1
echo
echo "#########################################################"
echo "# OK --> the PGP signature of the $testedJMversion tag is correct"
echo "#########################################################"
echo
fi
fi

View file

@ -3,7 +3,7 @@
# https://github.com/joinmarket-webui/jam
USERNAME=jam
WEBUI_VERSION="v0.4.0"
WEBUI_VERSION="v0.3.0"
REPO=joinmarket-webui/jam
HOME_DIR=/home/${USERNAME}
APP_DIR=webui

View file

@ -16,15 +16,9 @@ OPTIONS=()
BACKTITLE="JoininBox GUI"
# Basic Options
# Determine if using tag or commit for display
testedVersion=$(grep 'testedJMversion=' < ~/install.joinmarket.sh | grep -v '^#' | cut -d '"' -f 2)
if [ -z "$testedVersion" ]; then
testedVersion=$(grep 'testedJMcommit=' < ~/install.joinmarket.sh | cut -d '"' -f 2 | cut -c 1-12)
testedVersion="${testedVersion} (commit)"
fi
OPTIONS+=(
JOININBOX "Update the JoininBox scripts and menu"
JOINMARKET "Update/reinstall JoinMarket to ${testedVersion}")
JOINMARKET "Update/reinstall JoinMarket to $(grep testedJMversion= < ~/install.joinmarket.sh | cut -d '"' -f 2)")
if [ "$runningEnv" = "standalone" ]; then
OPTIONS+=(\

View file

@ -194,7 +194,7 @@ Enter the new gap limit to be used" 16 60 2> "$gaplimit"
echo
/home/joinmarket/start.script.sh wallet-tool "$(cat $wallet)"|grep mixdepth|sed -n '1~2p'|awk '{print $3}'
echo
echo "Import the master public keys to Specter Desktop or Electrum to create watch-only wallets."
echo "Import the master public keys to Specter Desktop or Electrum to create watch only wallets."
echo
echo "Press ENTER to return to the menu..."
read key

View file

@ -290,9 +290,9 @@ WantedBy=multi-user.target
echo "# Monitor the bitcoind with: sudo tail -f /home/bitcoin/.bitcoin/mainnet/debug.log"
echo
if [ ! -d /home/bitcoin/.bitcoin/wallets/watch-only-descriptor-wallet ]; then
echo "# Create watch-only-descriptor-wallet ..."
if [ ! -f /home/bitcoin/.bitcoin/mainnet/wallets/wallet.dat/wallet.dat ]; then
echo "# Create wallet.dat ..."
sleep 10
sudo -u bitcoin /usr/local/bin/bitcoin-cli -named createwallet wallet_name=watch-only-descriptor-wallet descriptors=true disable_private_keys=true
sudo -u bitcoin /usr/local/bin/bitcoin-cli -named createwallet wallet_name=wallet.dat descriptors=false
fi
}

View file

@ -43,18 +43,6 @@ Type=simple
TimeoutSec=infinity
Restart=no
# Memory limits (DoS / OOM protection)
MemoryHigh=300M
MemoryMax=512M
MemorySwapMax=0
# Reduce OOM kill priority (lower = less likely to be killed)
OOMScoreAdjust=-500
OOMPolicy=stop
# CPU limit
CPUQuota=80%
# Hardening measures
PrivateTmp=true
ProtectSystem=full

View file

@ -30,18 +30,9 @@ PGPsigner="$1"
PGPpubkeyLink="$2"
PGPpubkeyFingerprint="$3"
_temp_dir="$(mktemp -d -p /dev/shm/ 2>/dev/null || mktemp -d)"
trap 'rm -rf "$_temp_dir"' EXIT
keyFile="${_temp_dir}/pgp_keys_${PGPsigner}.asc"
rawKeyFile="${keyFile}.raw"
wget --prefer-family=ipv4 -O "${rawKeyFile}" "${PGPpubkeyLink}"
# GitHub can add a Note: armor header when an account key cannot be exported.
# GPG imports the key anyway, but prints a misleading "unknown armor header".
grep -v '^Note: ' "${rawKeyFile}" >"${keyFile}"
gpg --quiet --import --import-options show-only "${keyFile}"
fingerprint=$(gpg --show-keys --with-subkey-fingerprint "${keyFile}" 2>/dev/null | tr -d " \t\n\r" | grep "${PGPpubkeyFingerprint}" -c)
wget --prefer-family=ipv4 -O /dev/shm/pgp_keys_${PGPsigner}.asc "${PGPpubkeyLink}"
gpg --import --import-options show-only /dev/shm/pgp_keys_${PGPsigner}.asc
fingerprint=$(gpg --show-keys /dev/shm/pgp_keys_${PGPsigner}.asc 2>/dev/null | grep "${PGPpubkeyFingerprint}" -c)
if [ "${fingerprint}" -lt 1 ]; then
echo
echo "# WARNING --> the PGP fingerprint is not as expected for ${PGPsigner}" >&2
@ -49,9 +40,11 @@ if [ "${fingerprint}" -lt 1 ]; then
echo "# Exiting" >&2
exit 7
fi
gpg --quiet --import "${keyFile}"
gpg --import /dev/shm/pgp_keys_${PGPsigner}.asc
rm /dev/shm/pgp_keys_${PGPsigner}.asc
_temp="${_temp_dir}/git-verify.out"
trap 'rm -f "$_temp"' EXIT
_temp="$(mktemp -p /dev/shm/)"
if [ $# -eq 3 ] || [ -z "$4" ]; then
commitHash="$(git log --oneline | head -1 | awk '{print $1}')"

View file

@ -1,34 +0,0 @@
# JoininBox integration tests
Run the local Bats suite with:
```bash
test/run-bats-local.sh
```
The descriptor wallet tests require:
- `bats`
- `bitcoind`
- `bitcoin-cli`
- `curl`
- `jq`
The suite starts its own temporary `bitcoind -regtest` datadir and does not use
mainnet, signet, or any existing Bitcoin Core state.
The `amd64-image-test` workflow downloads a previously built
`joininbox-amd64-image-*` artifact, verifies the compressed and raw checksums,
decompresses a runner-local qcow2 copy, boots it with QEMU in snapshot mode,
copies a pinned `bats-core` checkout into that temporary VM session, and runs
the same suite from the JoininBox checkout inside the image. This avoids
depending on the guest's configured APT repositories just to install test
tooling.
The test workflow has two entry points:
- `workflow_run`: runs after a successful `amd64-image-build` once this workflow
exists on the repository default branch.
- `workflow_dispatch`: reruns against a specific build artifact by providing the
`amd64-image-build` workflow run ID, as long as the artifact is still retained
by GitHub Actions.

View file

@ -1,214 +0,0 @@
#!/usr/bin/env bats
root_dir="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
PATH="/home/joinmarket/bitcoin:/usr/local/bin:$PATH"
require_command() {
if ! command -v "$1" >/dev/null 2>&1; then
skip "$1 is required"
fi
}
setup() {
require_command bitcoind
require_command bitcoin-cli
require_command curl
require_command jq
rpc_user="joininbox"
rpc_pass="joininbox"
rpc_port="$((20000 + (RANDOM % 20000)))"
p2p_port="$((40000 + (RANDOM % 20000)))"
bitcoin_datadir="${BATS_TEST_TMPDIR}/bitcoin"
joinmarket_cfg="${BATS_TEST_TMPDIR}/joinmarket.cfg"
joinin_conf="${BATS_TEST_TMPDIR}/joinin.conf"
mkdir -p "$bitcoin_datadir"
bitcoind \
-regtest \
-datadir="$bitcoin_datadir" \
-server \
-daemonwait \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
-port="$p2p_port" \
-fallbackfee=0.0001
cat >"$joinmarket_cfg" <<EOF
rpc_user = $rpc_user
rpc_password = $rpc_pass
rpc_host = 127.0.0.1
rpc_port = $rpc_port
rpc_wallet_file = watch-only-descriptor-wallet
EOF
: >"$joinin_conf"
}
teardown() {
if [ -n "${bitcoin_datadir:-}" ] && [ -d "$bitcoin_datadir" ]; then
bitcoin-cli \
-regtest \
-datadir="$bitcoin_datadir" \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
stop >/dev/null 2>&1 || true
fi
}
load_joininbox_bitcoin_functions() {
# shellcheck source=scripts/_functions.bitcoincore.sh
# shellcheck disable=SC1091
source "$root_dir/scripts/_functions.bitcoincore.sh"
# shellcheck disable=SC2034
JMcfgPath="$joinmarket_cfg"
# shellcheck disable=SC2034
joininConfPath="$joinin_conf"
mktemp() {
if [ "${1:-}" = "-p" ] && [ "${2:-}" = "/dev/shm/" ]; then
command mktemp "${BATS_TEST_TMPDIR}/joininbox.XXXXXX"
else
command mktemp "$@"
fi
}
}
wallet_info() {
bitcoin-cli \
-regtest \
-datadir="$bitcoin_datadir" \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
-rpcwallet=watch-only-descriptor-wallet \
getwalletinfo
}
check_wallet_migration_with_enter() {
printf "\n" | checkWalletMigration
}
check_rpc_wallet_with_enter() {
printf "\n" | checkRPCwallet
}
@test "checkRPCwallet creates the configured descriptor watch-only wallet" {
load_joininbox_bitcoin_functions
run checkRPCwallet
[ "$status" -eq 0 ]
[[ "$output" == *"The wallet: watch-only-descriptor-wallet is present and loaded"* ]]
run wallet_info
[ "$status" -eq 0 ]
[ "$(jq -r '.descriptors' <<<"$output")" = "true" ]
[ "$(jq -r '.private_keys_enabled' <<<"$output")" = "false" ]
run grep -q "walletMigrationDone" "$joinin_conf"
[ "$status" -ne 0 ]
}
@test "customRPC uses the descriptor wallet RPC endpoint" {
bitcoin-cli \
-regtest \
-datadir="$bitcoin_datadir" \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
-named createwallet \
wallet_name=watch-only-descriptor-wallet \
descriptors=true \
disable_private_keys=true >/dev/null
load_joininbox_bitcoin_functions
run customRPC "# Wallet info" "getwalletinfo" ""
[ "$status" -eq 0 ]
[[ "$output" == *'"walletname": "watch-only-descriptor-wallet"'* ]]
[[ "$output" == *'"descriptors": true'* ]]
[[ "$output" == *'"private_keys_enabled": false'* ]]
}
@test "checkRPCwallet migrates a persisted wallet.dat configuration on Bitcoin Core v30 or later" {
bitcoin-cli \
-regtest \
-datadir="$bitcoin_datadir" \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
-named createwallet \
wallet_name=wallet.dat \
descriptors=true \
disable_private_keys=true >/dev/null
sed \
"s/^rpc_wallet_file =.*/rpc_wallet_file = wallet.dat/" \
"$joinmarket_cfg" >"${joinmarket_cfg}.legacy"
mv "${joinmarket_cfg}.legacy" "$joinmarket_cfg"
load_joininbox_bitcoin_functions
run check_rpc_wallet_with_enter
[ "$status" -eq 0 ]
[[ "$output" == *"Migrating the configured Bitcoin Core wallet"* ]]
[[ "$output" == *"WALLET MIGRATION NOTICE"* ]]
grep -q "^rpc_wallet_file = watch-only-descriptor-wallet$" "$joinmarket_cfg"
grep -q "^walletMigrationDone=true$" "$joinin_conf"
run wallet_info
[ "$status" -eq 0 ]
[ "$(jq -r '.descriptors' <<<"$output")" = "true" ]
[ "$(jq -r '.private_keys_enabled' <<<"$output")" = "false" ]
}
@test "migrateLegacyRPCWalletConfig keeps wallet.dat on Bitcoin Core v29.2" {
sed \
"s/^rpc_wallet_file =.*/rpc_wallet_file = wallet.dat/" \
"$joinmarket_cfg" >"${joinmarket_cfg}.legacy"
mv "${joinmarket_cfg}.legacy" "$joinmarket_cfg"
load_joininbox_bitcoin_functions
getConnectedBitcoinCoreVersion() {
echo 290200
}
getRPC >/dev/null
run migrateLegacyRPCWalletConfig
[ "$status" -eq 0 ]
[[ "$output" == *"v29.x or earlier; keeping wallet.dat"* ]]
grep -q "^rpc_wallet_file = wallet.dat$" "$joinmarket_cfg"
}
@test "checkWalletMigration shows the notice once when wallet.dat exists" {
bitcoin-cli \
-regtest \
-datadir="$bitcoin_datadir" \
-rpcuser="$rpc_user" \
-rpcpassword="$rpc_pass" \
-rpcport="$rpc_port" \
-named createwallet \
wallet_name=wallet.dat \
descriptors=true \
disable_private_keys=true >/dev/null
load_joininbox_bitcoin_functions
# shellcheck disable=SC2034
rpc_host="127.0.0.1"
# shellcheck disable=SC2034
rpc_wallet="watch-only-descriptor-wallet"
run check_wallet_migration_with_enter
[ "$status" -eq 0 ]
[[ "$output" == *"WALLET MIGRATION NOTICE"* ]]
grep -q "^walletMigrationDone=true$" "$joinin_conf"
run checkWalletMigration
[ "$status" -eq 0 ]
[ "$output" = "" ]
}

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
if ! command -v bats >/dev/null 2>&1; then
echo "bats is required. Install bats-core, then rerun this script." >&2
exit 127
fi
bats test/bats

View file

@ -7,5 +7,4 @@
[default.extend-words]
# don't correct these false positives
ba = "ba"
ned = "ned"
fpr = "fpr"
ned = "ned"