Compare commits

...

41 commits

Author SHA1 Message Date
openoms
16fc83f47d
chore: migrate the watch-only Bitcoin Core wallet to use descriptors (#177)
Some checks are pending
amd64-image-build / amd64-image-build (push) Waiting to run
arm64-rpi-image-build / arm64-rpi-image-build (push) Waiting to run
Test Shellcheck / Run Shellcheck (push) Waiting to run
* chore: update wallet creation to use watch-only-descriptor-wallet

* remove duplicate version check

* fix: update release URL in install script and correct typo in wallet import message

* ci: test amd64 image descriptor wallet migration with Bats

* ci: boot amd64 image tests with OVMF pflash

* fix: gate descriptor wallet migration on Bitcoin Core v30

Detect the connected Bitcoin Core version over RPC before migrating.
Keep wallet.dat for v29.x or when the version cannot be determined.

Add regression coverage for both migration and compatibility paths.

* ci: make amd64 image tests independent of guest apt

Inject a pinned bats-core checkout into the temporary VM instead of
installing Bats through the guest package repositories.

Remove the duplicate pull request trigger and simplify artifact lookup.

* ci: harden image build run

* docs(FAQ): how automatic migration works
2026-08-13 11:57:42 +02:00
openoms
54473190c8
chore: joinmarket-clientserver update to v0.9.12, use AdamISZ new code signing key (#185) 2026-04-26 15:37:17 +02:00
openoms
dfb33c8ab0
chore(standalone): bitcoin core update to v29.2 (#184) 2026-04-26 08:36:15 +02:00
openoms
aa8d79c97d
fix(service): add memory and CPU limits to reduce OOM kills (#182) 2026-03-29 22:36:56 +02:00
openoms
efdf660969
fix(build): use rolling Debian ISO, verify ISO checksum with PGP, add packer syntax checks (#181) 2026-03-29 15:31:05 +02:00
openoms
554a20a297
fix(stats): improve YGnickname function to handle log retrieval and output more robustly (#180) 2026-03-29 10:03:52 +02:00
openoms
7a61e3b357
update JoinMarket to commit ce32baf, support for Python 3.12 / 3.13, modernize build (#178)
This PR updates JoinMarket to the latest tested commit with Python 3.12/3.13 support and addresses dialog display issues.

### Changes

#### JoinMarket Update
- Update to commit `ce32baf` (latest tested commit from master)
- Refactored install.joinmarket.sh to support both tag-based and commit-based installations
- Improved signature verification using verify.git.sh
- Updated menu.update.sh to display commit hash when using commit-based version

#### Python Support
- Added support for Python 3.12 and 3.13
- Changed version detection order to prefer newer Python versions (3.13 → 3.12 → 3.11 → 3.10 → 3.9 → 3.8)

#### Dialog Fixes
- **Fix border visibility**: Changed dialog border color from `(BLACK,BLACK,ON)` to `(CYAN,BLACK,ON)` in .dialogrc for better visibility
- **Terminal compatibility**: Added fallback for unknown terminal types (e.g., `xterm-ghostty`) by setting `TERM=xterm-256color` when `infocmp` fails
- **Fix box drawing characters**: Set `NCURSES_NO_UTF8_ACS=1` to fix dialog box rendering issues

#### Documentation
- Fixed typo in prepare_remote_node.md: "numbes" → "numbers"
2026-01-18 07:56:26 +01:00
openoms
f92be4e5b3
docs: remove inactive donation links from readme (#176) 2025-06-09 21:51:39 +02:00
openoms
ea675689c5
jam remote update to v0.4.0 (#175) 2025-06-09 21:20:46 +02:00
openoms
e69d6006a4
feat: add shellcheck test to github actions (#174)
* feat: add shellcheck test to github actions
* fix: shellcheck errors
2025-06-06 23:10:54 +02:00
openoms
1b9eca2447
chore: add release-tag to makefile (#173) 2025-06-06 23:05:36 +02:00
openoms
a91cdc3657
chore: remove the boltzmann tool (#172) 2025-06-06 22:43:38 +02:00
openoms
56b06fafa4
chore: adapt to new path to bitcoin.conf in raspiblitz (#171) 2025-06-06 20:51:50 +02:00
openoms
304144e881
amd64 and arm64 base image updates (#167)
* amd64 base image update to debian-12.11.0-amd64-netinst.iso

* chore: update actions/upload-artifact to v4

* chore: build arm64-rpi on raspios_lite_arm64 only
2025-05-26 20:03:49 +02:00
openoms
b5bc21ad7b
jam-remote re-activate code signing check (#170)
* jam-remote re-activate code signing check

* chore(install.selfsignedcert.sh): non-interactive script
2025-05-26 00:26:11 +02:00
openoms
9594fbf20e
jam-remote update to 0.3.0 (#169) 2025-05-26 00:08:35 +02:00
openoms
44e095e324
fix: identify new location of raspiblitz.conf file (#168) 2025-05-25 23:30:11 +02:00
openoms
601eede8b5
amd64 base image update to debian-12.9.0-amd64-netinst.iso (#165) 2025-01-18 17:48:15 +01:00
openoms
19ffa45e66
add 64bit Raspberry Pi OS Lite base image for RPi5 (#161) 2025-01-13 23:37:24 +01:00
openoms
8afc78dcf4
add pip config set global.break-system-packages true to jm install (#164) 2025-01-13 23:36:29 +01:00
openoms
41a5c9e573
fix standalone bitcoind service (allow reach home folder) (#163) 2024-12-02 13:43:28 +01:00
openoms
b532dbeab3
docs: remove 32 bit SBC support 2024-12-02 13:40:47 +01:00
openoms
7800d632a5
Tweak logs and stats display (#162)
* stats display tweak
* maximize dialog prgbox views for logs
2024-11-23 08:00:30 +01:00
openoms
18d28bedf4
amd64 base image update to debian-12.8.0 (#160) 2024-11-22 21:46:59 +01:00
Arman The Parman
aaa22e375b
smol typo (#159) 2024-11-22 14:43:48 +01:00
openoms
c75ddacfc9
update amd64 base image to debian-12.7.0-amd64-netinst.iso (#157) 2024-09-13 17:32:28 +03:00
openoms
6b606760eb
correct menu option name 2024-09-13 10:58:56 +02:00
openoms
b9360c1008
docs: clarify jam remote steps 2024-07-15 17:48:39 +02:00
openoms
622847e80e
feat: add i2pd install script (#156)
* feat: add i2pd install script
* add comments to addseednodes
* limit addseednodes to 21 new random peers
2024-07-14 14:16:15 +02:00
openoms
c87e06c829
update amd64 base image to debian-12.6.0-amd64-netinst.iso (#155)
* update amd64 base image to debian-12.6.0-amd64-netinst.iso
* fix apt sources for amd64 image
2024-07-03 10:03:59 +02:00
openoms
efad4a01ff
Standalone setup fixes (#153)
* use bitcoin binary in /usr/local/bin/
* fix hostname and related warnings
* fix ownership of possibly migrated files
* build: fix fail2ban, remove boot drive tweak
* FAQ: remove outdated entries
2024-05-22 12:27:02 +02:00
openoms
e2e540ccbd
bitcoin core update to 26.1 (#152) 2024-04-11 16:58:10 +02:00
openoms
d961839137
prevent sleep and "unable to resolve host debian" error (#151)
* prevent sleep and "unable to resolve host debian" error

* fomat script
2024-04-05 08:33:08 +02:00
openoms
0126005b90
fixes for pruned node start (#149)
* increase required space for pruned chain download

* don't protect home as bitcoind binary is there

* format UDPATE menu
2024-03-10 21:39:14 +01:00
openoms
11c2e0dacd
change qtgui var to the correct option on install (#146) 2024-02-26 22:08:31 +01:00
openoms
dec26c7732
jam-remote update to 0.2.0 (#147) 2024-02-26 22:05:46 +01:00
openoms
ea5187c933
joinmarket update to v0.9.11 (#145)
* joinmarket update to v0.9.11
* remove deprecated install tweak
* remove pyhton 3.7 support
* stop using system-side PySide2
* don't install the QT Gui on ARM
* format start.joininbox.sh
* remove arm specific qtgui deps, no qtgui on build for arm
* don't protect system packages from pip install
* add libltdl-dev to apt-get packages
2024-02-26 12:06:00 +01:00
openoms
a61da715ca
build: fix github web-flow PGPpubkeyFingerprint (#144) 2024-02-14 08:44:38 +01:00
openoms
0daac40e21
ci: amd64 base image update to debian 12.5.0 (#143) 2024-02-13 10:24:08 +01:00
openoms
7b2166d124
build: debian update to 12.4.0-amd64 (#142) 2023-12-11 11:00:37 +01:00
openoms
8f61362f9b
Bitcoin Core update to v26.0 , add update option and display (#140)
* add aliases bitcoinlog, bitcoinconf
* add bitcoin.update.sh
* add update option, display bitcoin core version
* bitcoin core update to v26.0
* add deprecatedrpc=create_bdb, update services
fixes #141
* ci: update arm64-rpi image
* ci: install qemu plugin for packer
* ci: add vnc
* menu updates
2023-12-11 08:50:42 +01:00
54 changed files with 2068 additions and 788 deletions

15
.editorconfig Normal file
View file

@ -0,0 +1,15 @@
# 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,5 +1,9 @@
name: amd64-image-build
concurrency:
group: amd64-image-build-${{ github.head_ref }}
cancel-in-progress: true
on:
push:
branches: [ "master" ]
@ -61,7 +65,7 @@ jobs:
sha256sum joininbox-amd64-debian.qcow2.gz > joininbox-amd64-debian.qcow2.gz.sha256
- name: Upload the image and checksums
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: joininbox-amd64-image-${{ env.BUILD_DATE }}-${{ env.BUILD_VERSION }}
path: |

65
.github/workflows/amd64-image-test.yml vendored Normal file
View file

@ -0,0 +1,65 @@
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,5 +1,9 @@
name: arm64-rpi-image-build
concurrency:
group: arm64-rpi-image-build-${{ github.head_ref }}
cancel-in-progress: true
on:
push:
branches: [ "master" ]
@ -68,7 +72,7 @@ jobs:
sha256sum joininbox-arm64-rpi.img.gz > joininbox-arm64-rpi.img.gz.sha256
- name: Upload the image and checksums
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: joininbox-arm64-rpi-image-${{ env.BUILD_DATE }}-${{ env.BUILD_VERSION }}
path: |

View file

@ -0,0 +1,60 @@
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

19
.github/workflows/test-shellcheck.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: "Test Shellcheck"
on:
workflow_dispatch:
push:
branches: ["master"]
pull_request:
branches: ["master"]
jobs:
shellcheck:
name: Run Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:
severity: error

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
scripts/wallets
ci/arm64-rpi/.packer*
ci/arm64-rpi/joininbox-arm64-rpi*
ci/amd64/builds
scripts/jam-remote/download

187
FAQ.md
View file

@ -10,7 +10,6 @@
- [Log in through SSH using a hardware wallet](#log-in-through-ssh-using-a-hardware-wallet)
- [SSH through Tor from Linux](#ssh-through-tor-from-linux)
- [Allow Tor to connect to localhost](#allow-tor-to-connect-to-localhost)
- [Set up Armbian on the Hardkernel Odroid HC1 / XU4](#set-up-armbian-on-the-hardkernel-odroid-hc1--xu4)
- [Download and verify Raspbian SDcard image for a Raspberry Pi](#download-and-verify-raspbian-sdcard-image-for-a-raspberry-pi)
- [Error when connecting to a full node remotely through Tor](#error-when-connecting-to-a-full-node-remotely-through-tor)
- [Erase the joinmarket user and the /home/joinmarket folder](#erase-the-joinmarket-user-and-the-homejoinmarket-folder)
@ -31,6 +30,7 @@
- [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)
@ -172,55 +172,10 @@ Use `ssh` with `torsocks` on the desktop (needs Tor installed):
* Restart Tor:
`sudo systemctl restart tor`
## Set up Armbian on the Hardkernel Odroid HC1 / XU4
* Download the base image (`.img.gz`), the `.sha` and `.asc` file
https://www.armbian.com/odroid-xu4/
* Verify: https://docs.armbian.com/User-Guide_Getting-Started/#how-to-check-download-authenticity
```bash
gpg --keyserver ha.pool.sks-keyservers.net --recv-key DF00FAF1C577104B50BF1D0093D6889F9F0E78D5
# gpg: key 93D6889F9F0E78D5: public key "Igor Pecovnik # <igor@armbian. com>" imported
# gpg: Total number processed: 1
# gpg: imported: 1
gpg --verify Armbian_21.02.3_Odroidxu4_buster_legacy_4.14.222.img.xz.asc
# gpg: assuming signed data in 'Armbian_21.02.3_Odroidxu4_buster_legacy_4.14.222.img.xz'
# gpg: Signature made Tue 09 Mar 2021 03:00:30 GMT
# gpg: using RSA key DF00FAF1C577104B50BF1D0093D6889F9F0E78D5
# gpg: Good signature from "Igor Pecovnik <igor@armbian.com>" [unknown]
# gpg: aka "Igor Pecovnik (Ljubljana, Slovenia) <igor.pecovnik@gmail.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: DF00 FAF1 C577 104B 50BF 1D00 93D6 889F 9F0E 78D5
shasum -c Armbian_21.02.3_Odroidxu4_buster_legacy_4.14.222.img.xz.sha
# Armbian_21.02.3_Odroidxu4_buster_legacy_4.14.222.img.xz: OK
```
* Preparation
Make sure you have a good & reliable SD card and a proper power supply. Archives can be uncompressed with 7-Zip on Windows, Keka on OS X and 7z on Linux (apt-get install p7zip-full). RAW images can be written with Etcher (all OS).
* Boot
Insert the SD card into the slot, connect a cable to your network if possible or a display and power your board. (First) boot (with DHCP) takes up to 35 seconds with a class 10 SD Card.
* Login
Log in as: `root` Password: `1234`. Then you are prompted to change this password (US-Keyboard setting). When done, you are asked to create a normal user-account for your everyday tasks.
* Change the password.
* Create a new user called `joinmarket` and set the password (the password will be changed to `joininbox`).
Keep pressing [ENTER] to use the default user information.
* Continue to [install JoininBox](README.md#install-joininbox)
## Download and verify Raspbian SDcard image for a Raspberry Pi
To be able to open the JoinMarket-QT GUI on the desktop from the RPI
need to use the Raspberry Pi OS (32-bit) with desktop image
* Download image:
https://downloads.raspberrypi.org/raspios_armhf/images/raspios_armhf-2020-05-28/2020-05-27-raspios-buster-armhf.zip
* Download signature:
https://downloads.raspberrypi.org/raspios_armhf/images/raspios_armhf-2020-05-28/2020-05-27-raspios-buster-armhf.zip.sig
* Import PGP pubkey:
`curl https://www.raspberrypi.org/raspberrypi_downloads.gpg.key | gpg --import`
* Verify the image:
`gpg --verify 2020-05-27-raspios-buster-armhf.zip.sig`
* Flash the image to an SDcard, can use the [Raspberry Pi Imager](https://www.raspberrypi.org/downloads/)
* put a file called simply: `ssh` to the root of the sdcard.
Read more on [how to gain ssh access here](https://www.raspberrypi.org/documentation/remote-access/ssh/).
* boot up the RPi and log in with ssh to:
`pi@LAN_IP_ADDRESS`
The default password is: `raspberry`
* Download [Raspberry Pi Imager](https://www.raspberrypi.com/software/)
* enable the ssh login in the
* Flash the image to a min 32GB Endurance type SDcard
* Continue to [install JoininBox](README.md#install-joininbox)
## Error when connecting to a full node remotely through Tor
@ -241,7 +196,7 @@ The default password is: `raspberry`
server=1
daemon=1
disablewallet=0
main.wallet=wallet.dat
main.wallet=watch-only-descriptor-wallet
# Connection settings
rpcuser=REDACTED
@ -474,7 +429,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 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-descriptor-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`
@ -483,7 +438,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 watch only in the remote bitcoind.
in the joinmarket.cfg is the wallet which is used as a watch-only-descriptor-wallet 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`
@ -493,8 +448,67 @@ You need to run rescanblockchain on that wallet in bitcoind after importing the
Rescanning from the first SegWit block is sufficient for the default SegWit wallets.
* Monitor progress (on a RaspiBlitz):
`sudo tail -fn 100 /mnt/hdd/bitcoin/debug.log`
`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.
@ -565,35 +579,16 @@ 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 wallet.dat
# drwx------ 2 bitcoin bitcoin 4096 Mar 21 10:38 watch-only-descriptor-wallet
installMainnet
# Failed to stop bitcoind.service: Unit bitcoind.service not loaded.
#
# [Unit]
# Description=Bitcoin daemon on mainnet
# [Service]
# User=bitcoin
# Group=bitcoin
# Type=forking
# PIDFile=/home/bitcoin/bitcoin/bitcoind.pid
# ExecStart=/home/bitcoin/bitcoin/bitcoind -daemon -pid=/home/bitcoin/bitcoin/bitcoind.pid
# Restart=always
# TimeoutSec=120
# RestartSec=30
# StandardOutput=null
# StandardError=journal
#
# [Install]
# WantedBy=multi-user.target
#
# Created symlink /etc/systemd/system/multi-user.target.wants/bitcoind.service → /etc/systemd/system/bitcoind.service.
...
# # OK - the bitcoind.service is now enabled
#
# # Installed Bitcoin Core version v0.21.0
#
# # Monitor the bitcoind with: sudo tail -f /home/bitcoin/.bitcoin/mainnet/debug.log
#
# # Create wallet.dat ...
# # Create watch-only-descriptor-wallet ...
# error code: -28
# error message:
# Loading block index...
@ -604,48 +599,6 @@ Alternatively to a pruned node there could be a larger >400 GB storage connected
## IRC server settings
* See the most up to date configuration in:
https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/jmclient/jmclient/configure.py
* The latest setting with Tor enabled:
```
[MESSAGING:server1]
#host = irc.darkscience.net
channel = joinmarket-pit
port = 6697
usessl = true
#socks5 = false
socks5_host = localhost
socks5_port = 9050
#for tor
host = darkirc6tqgpnwd3blln3yfv5ckl47eg7llfxkmtovrv7c7iwohhb6ad.onion
socks5 = true
[MESSAGING:server2]
#host = irc.hackint.org
channel = joinmarket-pit
#port = 6697
#usessl = true
#socks5 = false
socks5_host = localhost
socks5_port = 9050
#for tor
host = ncwkrwxpq2ikcngxq3dy2xctuheniggtqeibvgofixpzvrwpa77tozqd.onion
port = 6667
usessl = false
socks5 = true
[MESSAGING:server3]
#host = agora.anarplex.net
channel = joinmarket-pit
#port = 14716
#usessl = true
#socks5 = false
socks5_host = localhost
socks5_port = 9050
#for tor
host = vxecvd6lc4giwtasjhgbrr3eop6pzq6i5rveracktioneunalgqlwfad.onion
port = 6667
usessl = false
socks5 = true
```
## Install JoinMarket without the QT GUI and dependencies
* Run the build script with the options `BRANCH` `GITHUBUSER` `without-qt`:

View file

@ -35,3 +35,12 @@ arm64-rpi-image:
# Compute checksum of the compressed image
cd ci/arm64-rpi && \
sha256sum joininbox-arm64-rpi.img.gz > joininbox-arm64-rpi.img.gz.sha256
release-tag:
@if [ -z "$(TAG)" ]; then \
echo "Error: TAG parameter is required. Usage: make release-tag TAG=v0.7.4"; \
exit 1; \
fi
@echo "Creating signed tag $(TAG) and pushing to origin..."
git tag -s $(TAG) -m "$(TAG)" && git push origin $(TAG)
@echo "Successfully created and pushed signed tag $(TAG)"

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 4 or 3](#raspberrypi-4-or-3)
- [RaspberryPi 5 or 4](#raspberrypi-5-or-4)
- [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,7 +32,6 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
- [More info](#more-info)
- [About JoinMarket](#about-joinmarket)
- [Forums](#forums)
- [Donations](#donations)
## Features
@ -44,19 +43,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 the RaspiBlitz SERVICES
* JoininBox is part of the RaspiBlitz SERVICES
**The addresses, transactions and balances of JoinMarket can be seen in the watch-only wallet of the connected node.**
**The addresses, transactions and balances of JoinMarket can be seen in the watch-only-descriptor-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 4 or 3
### RaspberryPi 5 or 4
* Power supply (5V 3A and above recommended)
* Heatsink case
* 32 GB Endurence type SDcard
* 32 GB Endurance 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:
@ -123,7 +122,7 @@ Recommended minimum:
### Install JoininBox
* Start as the `root` user or change with:
`$ sudo - su`
`$ sudo su -`
* Run the [build script](https://github.com/openoms/joininbox/blob/master/build_joininbox.sh):
```bash
@ -162,8 +161,3 @@ 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

@ -52,7 +52,7 @@ echo "
# https://github.com/${githubUser}/joininbox/tree/${wantedBranch}
# Press ENTER to confirm or CTRL+C to exit"
read key
read
echo
echo "###################################"
@ -90,7 +90,28 @@ echo "# Preparing the base image"
echo "############################"
echo
echo "# Prepare ${baseImage} "
echo "# Prevent sleep" # on all platforms https://wiki.debian.org/Suspend
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
mkdir /etc/systemd/sleep.conf.d
echo "[Sleep]
AllowSuspend=no
AllowHibernation=no
AllowSuspendThenHibernate=no
AllowHybridSleep=no" | tee /etc/systemd/sleep.conf.d/nosuspend.conf
mkdir /etc/systemd/logind.conf.d
echo "[Login]
HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore" | tee /etc/systemd/logind.conf.d/nosuspend.conf
# check if /etc/hosts already has debian entry
# prevent "unable to resolve host debian" error
isDebianInHosts=$(grep -c "debian" /etc/hosts)
if [ ${isDebianInHosts} -eq 0 ]; then
echo "# Adding debian to /etc/hosts"
echo "127.0.1.1 debian" | tee -a /etc/hosts > /dev/null
systemctl restart networking
fi
# special prepare on RPi
if [ "${baseimage}" = "raspios" ] || [ "${baseimage}" = "debian_rpi64" ] ||
[ "${baseimage}" = "armbian" ]; then
@ -139,17 +160,6 @@ if [ "${baseimage}" = "raspios" ] || [ "${baseimage}" = "debian_rpi64" ]; then
echo "$max_usb_current already in $configFile"
fi
# run fsck on sd root partition on every startup to prevent "maintenance login" screen
# see: https://github.com/rootzoll/raspiblitz/issues/782#issuecomment-564981630
# see https://github.com/rootzoll/raspiblitz/issues/1053#issuecomment-600878695
# use command to check last fsck check: sudo tune2fs -l /dev/mmcblk0p2
if [ "${tweak_boot_drive}" == "true" ]; then
echo "* running tune2fs"
tune2fs -c 1 /dev/mmcblk0p2
else
echo "* skipping tweak_boot_drive"
fi
# edit kernel parameters
kernelOptionsFile=/boot/cmdline.txt
fsOption1="fsck.mode=force"
@ -266,69 +276,46 @@ echo "##########"
echo
# apt dependencies for python
apt-get install -y python3 virtualenv python3-venv python3-dev python3-wheel python3-jinja2 python3-pip
if [ "${cpu}" = "armv7l" ] || [ "${cpu}" = "armv6l" ]; then
if [ ! -f "/usr/bin/python3.7" ]; then
# install python37
pythonVersion="3.7.9"
majorPythonVersion=$(echo "$pythonVersion" | awk -F. '{print $1"."$2}')
# dependencies
apt-get install software-properties-common build-essential libnss3-dev zlib1g-dev libgdbm-dev libncurses5-dev libssl-dev libffi-dev libreadline-dev libsqlite3-dev libbz2-dev -y
# download
wget --prefer-family=ipv4 --progress=bar:force https://www.python.org/ftp/python/${pythonVersion}/Python-${pythonVersion}.tgz
# optional signature for verification
wget --prefer-family=ipv4 --progress=bar:force https://www.python.org/ftp/python/${pythonVersion}/Python-${pythonVersion}.tgz.asc
# get PGP pubkey of Ned Deily (Python release signing key) <nad@python.org>
gpg --recv-key 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
# check for: Good signature from "Pablo Galindo Salgado <pablogsal@gmail.com>"
gpg --verify Python-${pythonVersion}.tgz.asc || exit 1
# unzip
tar xvf Python-${pythonVersion}.tgz
cd Python-${pythonVersion} || exit 1
# configure
./configure --enable-optimizations
# install
make altinstall
# move the python binary to the expected directory
mv "$(which python${majorPythonVersion})" /usr/bin/
# check
ls -la /usr/bin/python${majorPythonVersion} || exit 1
# clean
cd ..
rm Python-${pythonVersion}.tgz
rm -rf Python-${pythonVersion}
fi
update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1
echo "# python calls python3.7"
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"
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"
else
if [ -f "/usr/bin/python3.7" ]; then
# make sure /usr/bin/python exists (and calls Python3.7)
update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1
echo "# python calls python3.7"
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.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.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
fi
echo "# FAIL- there is no tested version of python present"
exit 1
fi
# make sure /usr/bin/pip exists (and calls pip3)
update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
# don't protect system packages from pip install
# tracking issue: https://github.com/raspiblitz/raspiblitz/issues/4170
for PYTHONDIR in /usr/lib/python3.*; do
if [ -f "$PYTHONDIR/EXTERNALLY-MANAGED" ]; then
rm "$PYTHONDIR/EXTERNALLY-MANAGED"
fi
done
# setuptools needed for Nyx
pip install setuptools
@ -359,6 +346,8 @@ apt-get install -y dialog
apt-get install -y qrencode
# unzip for the pruned node snapshot
apt-get install -y unzip
# JoinMarket dependency https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/5bfa08c6f558458c9a93e8095ce9dc1b62412838/install.sh#L76C10-L76C21
apt-get install -y libltdl-dev
apt-get clean
apt-get -y autoremove
@ -400,21 +389,21 @@ if echo "${lastCommit}" | grep 13C688DB5B9C745DE4D2E4545BFB77609B081B65; then
PGPsigner="openoms"
PGPpubkeyLink="https://github.com/openoms.gpg"
PGPpubkeyFingerprint="13C688DB5B9C745DE4D2E4545BFB77609B081B65"
elif echo "${lastCommit}" | grep 4AEE18F83AFDEB23; then
elif echo "${lastCommit}" | grep B5690EEEBB952194; then
echo "# The last commit was made on GitHub and is signed with the GitHub PGP key."
PGPsigner="web-flow"
PGPpubkeyLink="https://github.com/${PGPsigner}.gpg"
PGPpubkeyFingerprint="4AEE18F83AFDEB23"
PGPpubkeyFingerprint="B5690EEEBB952194"
else
echo "# No known PGP key found"
exit 1
fi
command="sudo -u joinmarket bash /home/joinmarket/joininbox/scripts/verify.git.sh \
command="bash /home/joinmarket/joininbox/scripts/verify.git.sh \
${PGPsigner} ${PGPpubkeyLink} ${PGPpubkeyFingerprint}"
echo "running: ${command}"
chmod 777 /dev/shm
${command} || exit 1
sudo -u joinmarket ${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"
@ -493,7 +482,7 @@ deb-src [arch=${arch}] https://deb.torproject.org/torproject.org ${distro} main"
apt-get install -y build-essential fakeroot devscripts
apt-get build-dep -y tor deb.torproject.org-keyring
mkdir ~/debian-packages
cd ~/debian-packages
cd ~/debian-packages || exit 1
apt-get source tor
cd tor-* || exit 1
debuild -rfakeroot -uc -us
@ -541,6 +530,7 @@ echo
apt-get install -y fail2ban ufw
# autostart fail2ban
systemctl enable fail2ban
touch /var/log/auth.log
# set up the firewall
ufw default deny incoming
@ -593,7 +583,6 @@ echo "#############"
echo "
if [ -f \"/home/joinmarket/joinmarket-clientserver/jmvenv/bin/activate\" ]; then
. /home/joinmarket/joinmarket-clientserver/jmvenv/bin/activate
/home/joinmarket/joinmarket-clientserver/jmvenv/bin/python -c \"import PySide2\"
cd /home/joinmarket/joinmarket-clientserver/scripts/
fi
# shortcut commands
@ -616,10 +605,16 @@ echo "######################"
echo "# Install JoinMarket"
echo "######################"
qtgui=true
if [ "${cpu}" = x86_64 ]; then
qtgui=true
else
# no qtgui on arm
qtgui=false
fi
checkEntry=$(runuser joinmarket -c "cat /home/joinmarket/joinin.conf | grep -c qtgui")
if [ ${checkEntry} -eq 0 ]; then
echo "qtgui=true" | tee -a /home/joinmarket/joinin.conf
echo "qtgui=$qtgui" | tee -a /home/joinmarket/joinin.conf
fi
if [ "$4" = "without-qt" ]; then
qtgui="false"

View file

@ -0,0 +1,120 @@
# 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

@ -1,47 +0,0 @@
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 httpredir.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

@ -0,0 +1,72 @@
# 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

@ -1,142 +0,0 @@
{
"variables": {
"iso_name": "debian-12.2.0-amd64-netinst.iso",
"iso_checksum": "23ab444503069d9ef681e3028016250289a33cc7bab079259b73100daee0af66",
"box_basename": "debian",
"build_directory": "../builds",
"cpus": "2",
"disk_size": "30000",
"headless": "false",
"http_directory": "{{template_dir}}/http",
"memory": "2048",
"mirror": "http://cdimage.debian.org/cdimage/release",
"mirror_directory": "current/amd64/iso-cd",
"name": "debian",
"preseed_path": "debian-9/preseed.cfg",
"qemu_display": "none",
"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>",
"version": "TIMESTAMP"
},
"builders": [
{
"boot_command": "{{user `boot_command`}}",
"boot_wait": "5s",
"cpus": "{{ user `cpus` }}",
"disk_size": "{{user `disk_size`}}",
"headless": "{{ user `headless` }}",
"http_directory": "{{user `http_directory`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{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",
"qemuargs": [
[ "-m", "{{ user `memory` }}" ],
[ "-bios", "{{ user `qemu_bios` }}" ],
[ "-display", "{{ user `qemu_display` }}" ]
]
},
{
"boot_command": "{{user `boot_command`}}",
"boot_wait": "5s",
"cpus": "{{ user `cpus` }}",
"disk_size": "{{user `disk_size`}}",
"guest_os_type": "Debian_64",
"hard_drive_interface": "sata",
"headless": "{{ user `headless` }}",
"http_directory": "{{user `http_directory`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
"memory": "{{ user `memory` }}",
"output_directory": "{{ user `build_directory` }}/packer-{{user `template`}}-virtualbox",
"shutdown_command": "echo 'joininbox' | sudo -S /sbin/shutdown -hP now",
"ssh_password": "joininbox",
"ssh_port": 22,
"ssh_timeout": "10000s",
"ssh_username": "joinmarket",
"type": "virtualbox-iso",
"virtualbox_version_file": ".vbox_version",
"vm_name": "{{ user `template` }}"
},
{
"boot_command": "{{user `boot_command`}}",
"boot_wait": "5s",
"cpus": "{{ user `cpus` }}",
"disk_size": "{{user `disk_size`}}",
"guest_os_type": "debian8-64",
"headless": "{{ user `headless` }}",
"http_directory": "{{user `http_directory`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
"memory": "{{ user `memory` }}",
"output_directory": "{{ user `build_directory` }}/packer-{{user `template`}}-vmware",
"shutdown_command": "echo 'joininbox' | sudo -S /sbin/shutdown -hP now",
"ssh_password": "joininbox",
"ssh_port": 22,
"ssh_timeout": "10000s",
"ssh_username": "joinmarket",
"tools_upload_flavor": "linux",
"type": "vmware-iso",
"vm_name": "{{ user `template` }}",
"vmx_data": {
"cpuid.coresPerSocket": "1",
"ethernet0.pciSlotNumber": "32"
},
"vmx_remove_ethernet_interfaces": true
},
{
"boot_command": "{{user `boot_command`}}",
"boot_wait": "5s",
"cpus": "{{ user `cpus` }}",
"disk_size": "{{user `disk_size`}}",
"guest_os_type": "debian",
"http_directory": "{{user `http_directory`}}",
"iso_checksum": "{{user `iso_checksum`}}",
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
"memory": "{{ user `memory` }}",
"output_directory": "{{ user `build_directory` }}/packer-{{user `template`}}-parallels",
"parallels_tools_flavor": "lin",
"prlctl_version_file": ".prlctl_version",
"shutdown_command": "echo 'joininbox' | sudo -S /sbin/shutdown -hP now",
"ssh_password": "joininbox",
"ssh_port": 22,
"ssh_timeout": "10000s",
"ssh_username": "joinmarket",
"type": "parallels-iso",
"vm_name": "{{ user `template` }}"
}
],
"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

@ -7,3 +7,6 @@ update-grub;
# Adding a 2 sec delay to the interface up, to make the dhclient happy
echo "pre-up sleep 2" >> /etc/network/interfaces
echo "$(hostname -I | awk '{print $1}') $(hostname)" >>/etc/hosts
echo "127.0.1.1 $(hostname)" >>/etc/hosts

View file

@ -1,36 +1,135 @@
#!/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 "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
# install qemu and UEFI firmware
echo "# Install qemu ..."
sudo apt-get update
sudo apt-get install -y qemu-system
sudo apt-get install -y qemu-system ovmf
# 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 "# Building image ..."
echo "# Build the image with: github_user=${github_user} branch=${branch}"
cd debian
PACKER_LOG=1 packer build \
-var github_user=${github_user} -var branch=${branch} \
-only=qemu joininbox-amd64-debian.json
packer init -upgrade .
PACKER_LOG=1 packer build ${vars} -only=qemu.debian build.amd64-debian.pkr.hcl || exit 1

138
ci/amd64/test.amd64-image-bats.sh Executable file
View file

@ -0,0 +1,138 @@
#!/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

@ -3,10 +3,10 @@ variable "branch" {}
source "arm" "joininbox-arm64-rpi" {
file_checksum_type = "sha256"
file_checksum = "a68cd2bfe7831c438d8a5d832803ae0db17afec9f3cd370d9e8748c7b5456283"
file_checksum = "62d025b9bc7ca0e1facfec74ae56ac13978b6745c58177f081d39fbb8041ed45"
file_target_extension = "xz"
file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"]
file_urls = ["https://raspi.debian.net/tested/20230612_raspi_4_bookworm.img.xz"]
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-05-13/2025-05-13-raspios-bookworm-arm64-lite.img.xz"]
image_build_method = "resize"
image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"]
image_partitions {
@ -39,7 +39,8 @@ build {
inline = [
"echo 'nameserver 1.1.1.1' >/etc/resolv.conf",
"echo 'nameserver 8.8.8.8' >>/etc/resolv.conf",
"echo $(hostname -I | awk '{print $1}') $(hostname) >>/etc/hosts",
"echo $(hostname -I | awk '{print $1}') $(hostname) >>/etc/hosts",
"echo 127.0.1.1 $(hostname) >>/etc/hosts",
"echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections",
"apt-get update",
"apt-get install -y sudo wget",

View file

@ -26,7 +26,7 @@ Since the RaspiBlitz v1.6 run this script:
To set up manually:
* Edit the bitcoin.conf:
`$ sudo nano /mnt/hdd/bitcoin/bitcoin.conf`
`$ sudo nano /mnt/hdd/app-data/bitcoin/bitcoin.conf`
* Change the disablewallet option to 0:
```
@ -40,12 +40,12 @@ In the terminal of the node - allow remote RPC connections to Bitcoin Core
This can be skipped if you [connect through Tor](#tor-connection)
1) Edit the bitcoin.conf
`$ sudo nano /mnt/hdd/bitcoin/bitcoin.conf`
`$ sudo nano /mnt/hdd/app-data/bitcoin/bitcoin.conf`
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 numbes 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 numbers 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`
@ -109,8 +109,8 @@ Fill in the `Tor_Hidden_Service.onion` to the `rpc_host` in the `joinmarket.cfg`
### CONFIG -> CONNECT in JoininBox
* Username: `raspibolt `
* Password: `passwordB` or `sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-`
* Host: `LAN_IP_OF_THE_NODE` or `sudo cat /mnt/hdd/tor/bitcoinrpc/hostname`
* Password: `passwordB` or `sudo cat /mnt/hdd/bitcoin/app-data/bitcoin.conf | grep rpcpassword | cut -c 13-`
* Host: `LAN_IP_OF_THE_NODE` or `sudo cat /mnt/hdd/app-data/tor/bitcoinrpc/hostname`
* Port: `8332`
---

View file

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

View file

@ -64,7 +64,7 @@ function torthistx() {
# command: stats
# shows the uptime and the fees earned as a Maker
function stats() {
/home/joinmarket/info.stats.sh
/home/joinmarket/info.stats.sh showAllEarned
}
# command: qtgui

View file

@ -9,7 +9,7 @@ joininConfPath="/home/joinmarket/joinin.conf"
function downloadBitcoinCore() {
# set version
# https://bitcoincore.org/en/download/
bitcoinVersion="25.0"
bitcoinVersion="29.2"
if bitcoin-cli --version | grep $bitcoinVersion >/dev/null; then
echo "# Bitcoin Core $bitcoinVersion is already installed"
@ -39,7 +39,7 @@ function downloadBitcoinCore() {
echo
else
echo
echo "# BUILD FAILED --> the PGP verification failed / signature(${goodSignature}) "
echo "# BUILD FAILED --> the PGP verification failed"
exit 1
fi
@ -155,25 +155,44 @@ function installSignet() {
Description=Bitcoin daemon on signet
[Service]
Environment='MALLOC_ARENA_MAX=1'
PIDFile=/home/joinmarket/bitcoin/bitcoind.pid
ExecStart=/home/joinmarket/bitcoin/bitcoind -signet -daemon \\
-datadir=/home/joinmarket/.bitcoin \\
-conf=/home/joinmarket/.bitcoin/bitcoin.conf \\
-pid=/home/joinmarket/bitcoin/bitcoind.pid
PermissionsStartOnly=true
# Process management
####################
Type=forking
Restart=on-failure
TimeoutStartSec=infinity
TimeoutStopSec=600
# Directory creation and permissions
####################################
# Run as joinmarket:joinmarket
User=joinmarket
Group=joinmarket
Type=forking
PIDFile=/home/joinmarket/bitcoin/bitcoind.pid
ExecStart=/home/joinmarket/bitcoin/bitcoind -signet -daemon \
-datadir=/home/joinmarket/.bitcoin \
-conf=/home/joinmarket/.bitcoin/bitcoin.conf \
-pid=/home/joinmarket/bitcoin/bitcoind.pid
Restart=always
TimeoutSec=120
RestartSec=30
StandardOutput=null
StandardError=journal
# Hardening measures
####################
# Provide a private /tmp and /var/tmp.
PrivateTmp=true
# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full
# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true
# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true
# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target
@ -228,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 = wallet.dat/g" $JMcfgPath
echo "# using the bitcoind wallet: wallet.dat"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = watch-only-descriptor-wallet/g" $JMcfgPath
echo "# using the bitcoind watch-only-descriptor-wallet"
# rpc_host
sed -i "s/^rpc_host =.*/rpc_host = 127.0.0.1/g" $JMcfgPath
echo "# rpc_host = 127.0.0.1"
@ -302,20 +321,79 @@ 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 "# 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 wallet in Bitcoin Core named $rpc_wallet"
echo "# Setting a watch-only-descriptor-wallet in Bitcoin Core named $rpc_wallet"
tor=""
if [ $(echo $rpc_host | grep -c .onion) -gt 0 ]; then
tor="torsocks"
@ -324,8 +402,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":false}}' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
'{"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 .
echo
walletFound=$(customRPC "# Check wallet" "listwallets" 2>$connectionOutput | grep -c "$rpc_wallet")
if [ $walletFound -eq 0 ]; then
@ -337,6 +415,75 @@ 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
@ -398,10 +545,16 @@ function connectLocalNode() {
elif [ "${network}" = testnet ]; then
rpc_port="18332"
fi
rpc_wallet="wallet.dat"
rpc_wallet="watch-only-descriptor-wallet"
if [ $runningEnv = raspiblitz ]; then
rpc_user=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
rpc_pass=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
rpc_user=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
rpc_pass=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
else
rpc_user=$(sudo cat /mnt/hdd/app-data/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
rpc_pass=$(sudo cat /mnt/hdd/app-data/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
fi
elif [ $runningEnv = mynode ]; then
rpc_user=mynode
rpc_pass=$(sudo cat /mnt/hdd/mynode/settings/.btcrpcpw)

View file

@ -37,7 +37,7 @@ function menu_MAKER() {
sleep 3
dialog \
--title "Monitoring the Yield Generator - press CTRL+C to exit" \
--prgbox "sudo journalctl -fn20 -u yg-privacyenhanced" 30 200
--prgbox "sudo journalctl -fn100 -u yg-privacyenhanced" -1 -1
echo "# returning to the menu..."
sleep 1
/home/joinmarket/menu.yg.sh

View file

@ -7,12 +7,21 @@ currentJBcommit=$(cd /home/joinmarket/joininbox; git describe --tags)
currentJBtag=$(cd /home/joinmarket/joininbox; git tag | sort -V | tail -1)
currentJMversion=$(cd /home/joinmarket/joinmarket-clientserver 2>/dev/null; \
git describe --tags 2>/dev/null)
currentBTCversion=$(bitcoind --version | grep version)
# paths
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
@ -22,7 +31,6 @@ fi
function activateJMvenv() {
. /home/joinmarket/joinmarket-clientserver/jmvenv/bin/activate || exit 1
/home/joinmarket/joinmarket-clientserver/jmvenv/bin/python -c "import PySide2"
}
function openMenuIfCancelled() {
@ -162,16 +170,16 @@ function stopYG() {
function YGnickname() {
# Retrieves nickname from the latest NickServ message in the newest logfile
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
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
name="no_Nick_see_LOGS"
fi
else
name="waiting__to__run"
fi
echo $name
echo "$name"
}
# copyJoininboxScripts
@ -249,28 +257,33 @@ function generateJMconfig() {
setJMconfigToSignet
elif [ -f "/mnt/hdd/bitcoin/bitcoin.conf" ]; then
elif [ $runningEnv = "raspiblitz" ]; then
echo
echo "## editing the joinmarket.cfg with the local bitcoin RPC settings."
RPCUSER=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
sed -i "s/^rpc_user =.*/rpc_user = $RPCUSER/g" $JMcfgPath
echo "# rpc_user = $RPCUSER"
PASSWORD_B=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
sed -i "s/^rpc_password =.*/rpc_password = $PASSWORD_B/g" $JMcfgPath
echo "# rpc_password = $PASSWORD_B"
RPCPORT=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep main.rpcport | cut -c 14-)
if [ ${#RPCPORT} -eq 0 ];then
if [ -f "/mnt/hdd/bitcoin/bitcoin.conf" ]; then
RPCUSER=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
sed -i "s/^rpc_user =.*/rpc_user = $RPCUSER/g" $JMcfgPath
echo "# rpc_user = $RPCUSER"
PASSWORD_B=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
sed -i "s/^rpc_password =.*/rpc_password = $PASSWORD_B/g" $JMcfgPath
echo "# rpc_password = $PASSWORD_B"
RPCPORT=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep main.rpcport | cut -c 14-)
else
RPCUSER=$(sudo cat /mnt/hdd/app-data/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
sed -i "s/^rpc_user =.*/rpc_user = $RPCUSER/g" $JMcfgPath
echo "# rpc_user = $RPCUSER"
PASSWORD_B=$(sudo cat /mnt/hdd/app-data/bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
sed -i "s/^rpc_password =.*/rpc_password = $PASSWORD_B/g" $JMcfgPath
echo "# rpc_password = $PASSWORD_B"
RPCPORT=$(sudo cat /mnt/hdd/app-data/bitcoin/bitcoin.conf | grep main.rpcport | cut -c 14-)
fi
if [ ${#RPCPORT} -eq 0 ]; then
RPCPORT=8332
fi
sed -i "s/^rpc_port =.*/rpc_port = $RPCPORT/g" $JMcfgPath
echo "# rpc_port = $RPCPORT"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = wallet.dat/g" $JMcfgPath
echo "# using the bitcoind wallet: wallet.dat"
sed -i "s/^rpc_wallet_file =.*/rpc_wallet_file = watch-only-descriptor-wallet/g" $JMcfgPath
echo "# using the bitcoind watch-only-descriptor-wallet"
# set joinin.conf value
/home/joinmarket/set.value.sh set network mainnet ${joininConfPath}
fi

View file

@ -36,7 +36,7 @@ Instructions to COPY wallets from another computer
You can use the wallets from another JoinMarket instance
"
if [ "${CHOICE}" = "LAN" ]; then
if [ -f "/mnt/hdd/raspiblitz.conf" ] ; then
if [ -f "/mnt/hdd/raspiblitz.conf" ] || [ -f "/mnt/hdd/app-data/raspiblitz.conf" ] ; then
echo "Both computers (the RaspiBlitz and the source computer with the wallet(s))"
else
echo "Both computers (the JoininBox and the source computer with the wallet(s))"
@ -62,9 +62,9 @@ elif [ "${CHOICE}" = "TOR" ]; then
echo "torsocks scp ./*.jmdat joinmarket@${TOR_ADDRESS}:~/.joinmarket/wallets/"
fi
echo ""
if [ -f "/mnt/hdd/raspiblitz.conf" ] ; then
if [ -f "/mnt/hdd/raspiblitz.conf" ] || [ -f "/mnt/hdd/app-data/raspiblitz.conf" ] ; then
echo "Use the PASSWORD_B to authorize the file transfer
(same as the rpcpassword in the /mnt/hdd/bitcoin/bitcoin.conf)."
(same as the rpcpassword in the bitcoin.conf)."
else
echo "This command will ask for the SSH PASSWORD of the JoininBox."
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 [ ! -f /home/${bitcoinUser}/.bitcoin/signet/wallets/wallet.dat/wallet.dat ];then
echo "# Create wallet.dat for signet ..."
if [ ! -d /home/${bitcoinUser}/.bitcoin/signet/wallets/watch-only-descriptor-wallet ]; then
echo "# Create watch-only-descriptor-wallet for signet ..."
sleep 10
sudo -u ${bitcoinUser} ${cliPath}/bitcoin-cli -signet -named createwallet wallet_name=wallet.dat descriptors=false
sudo -u ${bitcoinUser} ${cliPath}/bitcoin-cli -signet -named createwallet wallet_name=watch-only-descriptor-wallet descriptors=true disable_private_keys=true
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,14 +1,21 @@
#!/bin/bash
testedJMversion="v0.9.10"
# https://github.com/JoinMarket-Org/joinmarket-clientserver/releases
# Use tag for verification when available, otherwise use commit hash
testedJMversion="v0.9.12"
PGPsigner="kristapsk"
PGPpkeys="https://github.com/kristapsk.gpg"
PGPcheck="33E472FE870C7E5D"
# https://github.com/JoinMarket-Org/joinmarket-clientserver/commits/master/
# Only used if testedJMversion is empty or not set
#testedJMcommit="ce32bafbb5d716bde61830f71266410249d43dbc"
#PGPsigner="waxwing"
#PGPpkeys="https://raw.githubusercontent.com/JoinMarket-Org/joinmarket-clientserver/master/pubkeys/AdamGibson.asc"
#PGPcheck="2B6FC204D9BF332D062B461A141001A1AF77F20B"
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"
me="${0##/*}"
@ -20,13 +27,13 @@ usage() {
printf %s"${me} [--option <argument>]
a script to install, update or configure JoinMarket
the latest tested version: $testedJMversion is installed by default with the QT GUI
the latest tested version: ${testedJMversion:-$testedJMcommit} is installed by default with the QT GUI
Options:
-h, --help this help info
-i, --install [install|config|update|testPR|commit] install options, use 'commit' for the latest master
-v, --version [version|number-of-PR] the version to install or PR to test (default: ${testedJMversion})
-q, --qtgui [0|1] install the QT GUI and dependencies (default: 1)
-q, --qtgui [0|1] install the QT GUI and dependencies (default: 0)
-u, --user [user] the linux user to install with (default: joinmarket)
Notes:
@ -109,10 +116,13 @@ done
: "${install:=install}"
range_argument install "install" "config" "update" "testPR" "commit"
: "${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:=true}"
# 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
: "${qtgui:=false}"
range_argument qtgui "0" "1" "false" "true"
: "${user:=joinmarket}"
@ -140,7 +150,6 @@ if [ "${user}" != "joinmarket" ]; then
echo "
if [ -f \"/home/${user}/joinmarket-clientserver/jmvenv/bin/activate\" ]; then
. /home/${user}/joinmarket-clientserver/jmvenv/bin/activate
/home/${user}/joinmarket-clientserver/jmvenv/bin/python -c \"import PySide2\"
cd /home/${user}/joinmarket-clientserver/scripts/
fi
" | sudo -u ${user} tee -a /home/${user}/.bashrc
@ -152,22 +161,14 @@ checkEntry=$(sudo -u ${user} cat /home/${user}/joinin.conf | grep -c "qtgui")
if [ ${checkEntry} -eq 0 ]; then
echo "qtgui=true" | sudo -u ${user} tee -a /home/${user}/joinin.conf
fi
if [ "${qtgui}" = "false" ]; then
sudo -u ${user} sed -i "s/^qtgui=.*/qtgui=false/g" /home/${user}/joinin.conf
fi
sudo -u ${user} sed -i "s/^qtgui=.*/qtgui=${qtgui}/g" /home/${user}/joinin.conf
# installJoinMarket [update|testPR <PRnumber>|commit]
function installJoinMarket() {
cpu=$(uname -m)
cd /home/${user} || exit 1
if [ "${qtgui}" = "true" ]; then
# PySide2 for armf: https://packages.debian.org/buster/python3-pyside2.qtcore
echo "# Installing ARM specific dependencies to run the QT GUI"
sudo apt-get install -y python3-pyside2.qtcore python3-pyside2.qtgui \
python3-pyside2.qtwidgets zlib1g-dev libjpeg-dev python3-pyqt5 libltdl-dev
fi
# https://github.com/JoinMarket-Org/joinmarket-clientserver/issues/668#issuecomment-717815719
sudo apt-get install -y build-essential automake pkg-config libffi-dev python3-dev
sudo -u ${user} pip config set global.break-system-packages true
sudo -u ${user} pip install libtool asn1crypto cffi pycparser
echo "# Installing JoinMarket"
@ -191,37 +192,29 @@ function installJoinMarket() {
elif [ "$install" = "update" ] && [ ${#2} -gt 0 ]; then
updateVersion="$2"
sudo -u ${user} git reset --hard $updateVersion
else
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 key
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
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
echo
echo "#########################################################"
echo "# OK --> the PGP signature of the $testedJMversion tag is correct"
echo "#########################################################"
echo
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
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
fi
fi
@ -232,30 +225,6 @@ function installJoinMarket() {
fi
# do not clear screen during installation
sudo -u ${user} sed -i 's/clear//g' install.sh
# do not stop at installing Debian dependencies
sudo -u ${user} sed -i \
"s#^ if ! sudo apt-get install \${deb_deps\[@\]}; then#\
if ! sudo apt-get install -y \${deb_deps\[@\]}; then#g" install.sh
if [ ${cpu} != "x86_64" ]; then
echo "# Make install.sh set up jmvenv with -- system-site-packages on arm"
# and import the PySide2 armf package from the system
sudo -u ${user} sed -i "s#^ virtualenv -p \"\${python}\" \"\${jm_source}/jmvenv\" || return 1#\
virtualenv --system-site-packages -p \"\${python}\" \"\${jm_source}/jmvenv\" || return 1 ;\
/home/${user}/joinmarket-clientserver/jmvenv/bin/python -c \'import PySide2\'\
#g" install.sh
# don't install PySide2 - using the system-site-package instead
sudo -u ${user} sed -i "s#^PySide2.*##g" requirements/gui.txt
# don't install PyQt5 - using the system package instead
sudo -u ${user} sed -i "s#^PyQt5.*##g" requirements/gui.txt
sudo -u ${user} sed -i "s#PyQt5!=5.15.0,!=5.15.1,!=5.15.2,!=6.0##g" jmqtui/setup.py
fi
# pin werkzeug dependency to 2.2.0 as in:
# https://github.com/JoinMarket-Org/joinmarket-clientserver/pull/1485/files
if ! grep 'werkzeug==' jmclient/setup.py; then
sed -i "s/autobahn==20.12.3/&', 'werkzeug==2.2.0/g" jmclient/setup.py
fi
if [ "${qtgui}" = "false" ]; then
GUIchoice="--without-qt"

View file

@ -1,33 +1,38 @@
# Install Jam and connect to a remote Joininbox
* tested on Debian Bullseye desktop - Ubuntu should also work
* tested on Debian Bullseye and Bookworm desktop - Ubuntu should also work
### Download the repo
```
git clone https://github.com/openoms/joininbox
cd joininbox
```
### Install Jam locally
* will be under the user: `jam`
```
cd scripts/jam-remote
bash install.jam.sh on
```
### On your Joininbox
## Steps in your Joininbox terminal
* (optional) update the joininbox scripts: `UPDATE` - `ADVANCED` - `JBCOMMIT`
* start the `API` from `TOOLS`
* (optional) start the ob-watcher from `OFFERS`
## Steps on your desktop
### Download the repo
* move to a directory where the joininbox repo will be stored
```
git clone https://github.com/openoms/joininbox
cd joininbox
```
### Install Jam locally
* will be under the user: `jam`
```
cd scripts/jam-remote
bash install.jam.sh on
```
### Forward the API and ob-watcher ports with ssh from your Joininbox
```
bash ssh-portforward $JOININBOX_LAN_IP
```
* run the ssh-port-forward script in the `joininbox/scripts/jam-remote` folder
```
bash ./ssh-port-forward.sh $JOININBOX_LAN_IP
```
* leave this terminal open until working with Jam
* close when done to close the ssh connection
* Earn will continue to run on the Joininbox
* can check it's logs in the menu - `MAKER` - `LOGS`
### Open Jam locally using the wallets on your remote Joininbox
`https://localhost:7501`
* open Jam at https://localhost:7501
* accept the self-signed certificate served from your Joininbox
* Use Jam - docs: https://jamdocs.org/
* Earn will continue to run on the Joininbox even after the terminal and Jam windows are closed
* can check the logs in the Joininbox menu - `MAKER` - `LOGS`

View file

@ -3,15 +3,15 @@
# https://github.com/joinmarket-webui/jam
USERNAME=jam
WEBUI_VERSION="v0.1.6"
WEBUI_VERSION="v0.4.0"
REPO=joinmarket-webui/jam
HOME_DIR=/home/${USERNAME}
APP_DIR=webui
SOURCEDIR=$(pwd)
PGPsigner="dergigi"
PGPsigner="theborakompanioni"
PGPpubkeyLink="https://github.com/${PGPsigner}.gpg"
PGPpubkeyFingerprint="89C4A25E69A5DE7F"
PGPpubkeyFingerprint="E8070AF0053AAC0D"
# command info
if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "-help" ]; then
@ -59,8 +59,8 @@ if [ "$1" = "on" ]; then
cd jam || exit 1
sudo -u $USERNAME git reset --hard ${WEBUI_VERSION}
#sudo -u $USERNAME bash ${SOURCEDIR}/../verify.git.sh \
# "${PGPsigner}" "${PGPpubkeyLink}" "${PGPpubkeyFingerprint}" "v${WEBUI_VERSION}" || exit 1
sudo -u $USERNAME bash ${SOURCEDIR}/../verify.git.sh \
"${PGPsigner}" "${PGPpubkeyLink}" "${PGPpubkeyFingerprint}" "${WEBUI_VERSION}" || exit 1
cd $HOME_DIR || exit 1
sudo -u $USERNAME mv jam $APP_DIR

View file

@ -3,10 +3,10 @@
USERNAME=jam
sudo apt install nginx
sudo apt-get install -y nginx
if ! sudo ls /home/jam/nginx/tls.cert || ! sudo ls /home/jam/nginx/tls.key; then
sudo apt-get install openssl
sudo apt-get install -y openssl
subj="/C=US/ST=Utah/L=Lehi/O=Your Company, Inc./OU=IT/CN=example.com"
sudo -u $USERNAME mkdir -p /home/jam/nginx/ \

View file

@ -10,7 +10,11 @@ if [ ${#network} -eq 0 ] || [ "${network}" = "unknown" ] ;then
elif [ "${runningEnv}" = mynode ];then
network=mainnet
elif [ "${runningEnv}" = raspiblitz ];then
source /mnt/hdd/raspiblitz.conf
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
source /mnt/hdd/raspiblitz.conf
else
source /mnt/hdd/app-data/raspiblitz.conf
fi
if [ $network = bitcoin ];then
network=${chain}net
else

View file

@ -1,7 +1,11 @@
#!/bin/bash
source /home/joinmarket/joinin.conf
source /mnt/hdd/raspiblitz.conf
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
source /mnt/hdd/raspiblitz.conf
else
source /mnt/hdd/app-data/raspiblitz.conf
fi
# BASIC MENU INFO
HEIGHT=8

View file

@ -90,7 +90,7 @@ function startOrderBook() {
sleep 3
dialog \
--title "Monitoring the ob-watcher - press CTRL+C to exit" \
--prgbox "sudo journalctl -fn20 -u ob-watcher" 30 200
--prgbox "sudo journalctl -fn100 -u ob-watcher" -1 -1
}
if [ "$1" = startOrderBookService ]; then

View file

@ -26,17 +26,6 @@ function listCJcandidateTXNs {
done
}
function installBoltzmann {
if [ ! -f "/home/joinmarket/boltzmann/bvenv/bin/activate" ]; then
cd /home/joinmarket/ || exit 1
git clone https://code.samourai.io/oxt/boltzmann.git
cd boltzmann || exit 1
python3 -m venv bvenv
source bvenv/bin/activate || exit 1
python setup.py install
fi
}
function dialog_inputbox {
local title=$1
local text=$2
@ -66,7 +55,7 @@ isLocalBitcoinCLI=$(sudo -u bitcoin bitcoin-cli -version | grep -c "Bitcoin Core
isTxindex=$(sudo -u bitcoin cat /home/bitcoin/.bitcoin/bitcoin.conf | grep -c "txindex=1")
# BASIC MENU INFO
HEIGHT=13
HEIGHT=12
WIDTH=61
CHOICE_HEIGHT=7
TITLE="Tools"
@ -97,8 +86,6 @@ if [ "$isLocalBitcoinCLI" -gt 0 ] && [ "$isTxindex" -gt 0 ]; then
CHOICE_HEIGHT=$((CHOICE_HEIGHT + 1))
fi
fi
OPTIONS+=(
BOLTZMANN "Analyze the entropy of a transaction")
if [ "${runningEnv}" != mynode ]; then
OPTIONS+=(
PASSWORD "Change the ssh password")
@ -186,15 +173,6 @@ CHECKTXN)
echo "Press ENTER to return to the menu..."
read key
;;
BOLTZMANN)
dialog_inputbox "Boltzmann transaction entropy analysis" "\nUsing: https://code.samourai.io/oxt/boltzmann\n\nPaste a TXID to analyze" 11 71
clear
installBoltzmann
python /home/joinmarket/start.boltzmann.py --txid=$dialog_output
echo
echo "Press ENTER to return to the menu..."
read key
;;
CJFINDER)
BLOCKHEIGHT=$(customRPC "" "getblockchaininfo" "" |
grep blocks | awk '{print $2}' | cut -d, -f1)

View file

@ -3,13 +3,16 @@
source /home/joinmarket/_functions.sh
# BASIC MENU INFO
HEIGHT=16
HEIGHT=18
WIDTH=60
CHOICE_HEIGHT=7
TITLE="Advanced update options"
MENU="
Current JoininBox version: $currentJBcommit
Current JoinMarket version: $currentJMversion"
Installed versions:
JoininBox $currentJBcommit
JoinMarket $currentJMversion
$currentBTCversion"
OPTIONS=()
BACKTITLE="JoininBox GUI"

View file

@ -3,20 +3,34 @@
source /home/joinmarket/_functions.sh
# BASIC MENU INFO
HEIGHT=12
WIDTH=56
CHOICE_HEIGHT=3
HEIGHT=15
WIDTH=57
CHOICE_HEIGHT=4
TITLE="Update options"
MENU="
Current JoininBox version: $currentJBcommit
Current JoinMarket version: $currentJMversion"
Installed versions:
JoininBox $currentJBcommit
JoinMarket $currentJMversion
$currentBTCversion"
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 $(grep testedJMversion= < ~/install.joinmarket.sh | cut -d '"' -f 2)"
JOINMARKET "Update/reinstall JoinMarket to ${testedVersion}")
if [ "$runningEnv" = "standalone" ]; then
OPTIONS+=(\
BITCOIN "Update Bitcoin Core to a chosen version")
fi
OPTIONS+=(\
ADVANCED "Advanced update options")
CHOICE=$(dialog --clear \
@ -47,6 +61,14 @@ case $CHOICE in
echo
echo "Press ENTER to return to the menu"
read key;;
BITCOIN)
/home/joinmarket/standalone/bitcoin.update.sh custom
errorOnInstall $?
echo
echo "# Start bitcoind .. "
sudo systemctl start bitcoind
echo "# Monitoring the bitcoind logs .. "
showBitcoinLogs;;
ADVANCED)
/home/joinmarket/menu.update.advanced.sh;;
esac

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

@ -73,9 +73,9 @@ case $CHOICE in
--msgbox "
There are no stats because the Yield Generator was never run.
Start with the menu option: RUN_YG" 10 50
Start with the menu option: MAKER" 10 50
else
dialog --prgbox "/home/joinmarket/info.stats.sh showAllEarned" 9 55
dialog --prgbox "/home/joinmarket/info.stats.sh showAllEarned" 9 67
fi;;
NICKNAME)
name=$(YGnickname)

View file

@ -1,6 +1,6 @@
#!/bin/bash
if [ ${#1} -eq 0 ]||[ $1 = "-h" ]||[ $1 = "--help" ];then
if [ ${#1} -eq 0 ] || [ $1 = "-h" ] || [ $1 = "--help" ]; then
echo "Enable or disable ssh access with the joinmarket user"
echo "sudo set.ssh.sh [off|on]"
echo
@ -18,7 +18,7 @@ if ! grep -Eq "^joinmarketSSH=" /home/joinmarket/joinin.conf; then
fi
echo
if [ "$1" = "off" ];then
if [ "$1" = "off" ]; then
echo "# Disable ssh access with the joinmarket user"
if ! grep -Eq "^DenyUsers joinmarket" /etc/ssh/sshd_config; then
echo "DenyUsers joinmarket" | tee -a /etc/ssh/sshd_config
@ -35,4 +35,4 @@ elif [ "$1" = "on" ]; then
else
echo "# Invalid option $*"
exit 1
fi
fi

View file

@ -30,7 +30,7 @@ if [ "$1" = "set" ]; then
# check that config file exists
raspiblitzConfExists=$(ls ${configFile} 2>/dev/null | grep -c "${configFile}")
if [ ${raspiblitzConfExists} -eq 0 ]; then
echo "# blitz.conf.sh $@"
echo "# blitz.conf.sh $*"
echo "# FAIL: missing config file: ${configFile}"
exit 3
fi

View file

@ -49,13 +49,13 @@ function downloadSnapShot() {
echo "# Check available diskspace"
FREE=$(df -k --output=avail "$PWD" | tail -n1) # df -k not df -h
if [ $FREE -lt 12582912 ]; then # 12G = 12*1024*1024k
if [ $FREE -lt 21000000 ]; then
echo "# The free space is only $FREE bytes!"
echo "# Would need ~12GB free space to download and extract the snapshot."
echo "# Would need ~21GB free space to download and extract the snapshot."
echo "# Press ENTER to continue to download regardless or CTRL+C to exit."
read key
else
echo "# OK, more than 12GB is free!"
echo "# OK, more than 21GB is free!"
fi
sudo -u joinmarket mkdir /home/joinmarket/download 2>/dev/null
cd /home/joinmarket/download || exit 1
@ -107,13 +107,13 @@ function downloadSnapShot() {
echo "# Extracting to /home/store/app-data/.bitcoin ..."
FREE=$(df -k --output=avail "$PWD" | tail -n1) # df -k not df -h
if [ $FREE -lt 7340032 ]; then # 7G = 7*1024*1024k
if [ $FREE -lt 11000000 ]; then
echo "# The free space is only $FREE bytes!"
echo "# Would need ~7GB free space to extract the snapshot."
echo "# Press ENTER to continue to download regardless or CTRL+C to exit."
echo "# Would need ~11GB free space to extract the snapshot."
echo "# Press ENTER to continue to extract it regardless or CTRL+C to exit."
read key
else
echo "# OK, more than 7GB is free!"
echo "# OK, more than 11GB is free!"
fi
addUserStore
if [ ! -d /home/store/app-data/.bitcoin ]; then
@ -140,7 +140,11 @@ function downloadSnapShot() {
echo "# Unzip ..."
sudo apt-get install -y unzip
sudo -u bitcoin unzip -o $downloadFileName -d /home/store/app-data/.bitcoin
sudo -u bitcoin unzip -o $downloadFileName -d /home/store/app-data/.bitcoin || exit 1
echo "# OK - Decompressed successfully"
echo "# Removing the downloaded files"
rm -f $hashFileName
rm -f $downloadFileName
if sudo -u bitcoin ls /home/bitcoin/.bitcoin/bitcoin.conf.backup; then
echo "# Restore bitcoin.conf"
sudo -u bitcoin mv -f /home/bitcoin/.bitcoin/bitcoin.conf.backup \
@ -151,8 +155,8 @@ function downloadSnapShot() {
function installBitcoinCoreStandalone() {
downloadBitcoinCore
if [ -f /home/bitcoin/bitcoin/bitcoind ]; then
installedVersion=$(/home/bitcoin/bitcoin/bitcoind --version | grep version)
if [ -f /usr/local/bin/bitcoind ]; then
installedVersion=$(/usr/local/bin/bitcoind --version | grep version)
echo "${installedVersion} is already installed"
else
echo "# Adding the user: bitcoin"
@ -162,15 +166,10 @@ function installBitcoinCoreStandalone() {
echo "# Add the joinmarket user to the bitcoin group"
sudo usermod -aG bitcoin joinmarket
echo "# Installing Bitcoin Core v${bitcoinVersion}"
sudo -u bitcoin mkdir -p /home/bitcoin/bitcoin
cd /home/joinmarket/download/bitcoin-${bitcoinVersion}/bin/ || exit 1
sudo install -m 0755 -o root -g root -t /home/bitcoin/bitcoin ./*
sudo install -m 0755 -o root -g root -t /usr/local/bin/ ./*
fi
if [ "$(grep -c "/home/bitcoin/bitcoin" </etc/profile)" -eq 0 ]; then
echo "# Add /home/bitcoin/bitcoin to global PATH"
echo "PATH=/home/bitcoin/bitcoin:$PATH" | sudo tee -a /etc/profile
fi
if ! sudo -u bitcoin /home/bitcoin/bitcoin/bitcoind --version | grep "Bitcoin Core version"; then
if ! sudo -u bitcoin /usr/local/bin/bitcoind --version | grep "Bitcoin Core version"; then
echo
echo "# BUILD FAILED --> Was not able to install Bitcoin Core)"
exit 1
@ -220,24 +219,45 @@ function installMainnet() {
echo "
[Unit]
Description=Bitcoin daemon on mainnet
[Service]
Environment='MALLOC_ARENA_MAX=1'
ExecStart=/usr/local/bin/bitcoind \\
-daemonwait \\
-conf=/home/bitcoin/.bitcoin/bitcoin.conf \\
-datadir=/home/bitcoin/.bitcoin
PermissionsStartOnly=true
# Process management
####################
Type=forking
Restart=on-failure
TimeoutStartSec=infinity
TimeoutStopSec=600
# Directory creation and permissions
####################################
# Run as bitcoin:bitcoin
User=bitcoin
Group=bitcoin
Type=forking
PIDFile=/home/bitcoin/bitcoin/bitcoind.pid
ExecStart=/home/bitcoin/bitcoin/bitcoind -daemon \
-pid=/home/bitcoin/bitcoin/bitcoind.pid
Restart=always
TimeoutSec=120
RestartSec=30
StandardOutput=null
StandardError=journal
# Hardening measures
####################
# Provide a private /tmp and /var/tmp.
PrivateTmp=true
# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full
# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true
# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true
# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target
@ -247,9 +267,17 @@ WantedBy=multi-user.target
echo "# OK - the bitcoind.service is now enabled"
# add aliases
if [ $(alias | grep -c "sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli") -eq 0 ]; then
sudo bash -c "echo 'alias bitcoin-cli=\"sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli\"' >> /home/joinmarket/_aliases.sh"
sudo bash -c "echo 'alias bitcoind=\"sudo -u bitcoin /home/bitcoin/bitcoin/bitcoind\"' >> /home/joinmarket/_aliases.sh"
if ! grep "alias bitcoin-cli" /home/joinmarket/_aliases.sh; then
sudo bash -c "echo 'alias bitcoin-cli=\"sudo -u bitcoin /usr/local/bin/bitcoin-cli\"' >> /home/joinmarket/_aliases.sh"
fi
if ! grep "alias bitcoind" /home/joinmarket/_aliases.sh; then
sudo bash -c "echo 'alias bitcoind=\"sudo -u bitcoin /usr/local/bin/bitcoind\"' >> /home/joinmarket/_aliases.sh"
fi
if ! grep "alias bitcoinlog" /home/joinmarket/_aliases.sh; then
sudo bash -c "echo 'alias bitcoinlog=\"sudo tail -f /home/bitcoin/.bitcoin/debug.log\"' >> /home/joinmarket/_aliases.sh"
fi
if ! grep "alias bitcoinconf" /home/joinmarket/_aliases.sh; then
sudo bash -c "echo 'alias bitcoinconf=\"sudo nano /home/bitcoin/.bitcoin/bitcoin.conf\"' >> /home/joinmarket/_aliases.sh"
fi
# set joinin.conf value
@ -257,14 +285,14 @@ WantedBy=multi-user.target
sudo systemctl start bitcoind
echo
echo "# Installed $(sudo -u bitcoin /home/bitcoin/bitcoin/bitcoind --version | grep version)"
echo "# Installed $(sudo -u bitcoin /usr/local/bin/bitcoind --version | grep version)"
echo
echo "# Monitor the bitcoind with: sudo tail -f /home/bitcoin/.bitcoin/mainnet/debug.log"
echo
if [ ! -f /home/bitcoin/.bitcoin/mainnet/wallets/wallet.dat/wallet.dat ]; then
echo "# Create wallet.dat ..."
if [ ! -d /home/bitcoin/.bitcoin/wallets/watch-only-descriptor-wallet ]; then
echo "# Create watch-only-descriptor-wallet ..."
sleep 10
sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -named createwallet wallet_name=wallet.dat descriptors=false
sudo -u bitcoin /usr/local/bin/bitcoin-cli -named createwallet wallet_name=watch-only-descriptor-wallet descriptors=true disable_private_keys=true
fi
}

View file

@ -0,0 +1,219 @@
#!/bin/bash
# command info
if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "-help" ]; then
echo "Interim optional Bitcoin Core updates."
echo "bitcoin.update.sh [info|tested|reckless|custom]"
echo "info -> get actual state and possible actions"
echo "tested -> only do a tested update by the RaspiBlitz team"
echo "reckless -> the update was not tested by the RaspiBlitz team"
echo "custom <version> <skipverify> -> update to a chosen version"
echo " the binary checksum and signatures will be checked in all cases"
echo " except when 'skipverify' is used"
echo
exit 1
fi
echo "# Running: bitcoin.update.sh $*"
# 1. parameter [info|tested|reckless]
mode="$1"
# RECOMMENDED UPDATE BY RASPIBLITZ TEAM (just possible once per sd card update)
# comment will be shown as "BEWARE Info" when option is chosen (can be multiple lines)
bitcoinVersion="" # example: 22.0 .. keep empty if no newer version as sd card build is available
# GATHER DATA
# setting download directory to the current user
downloadDir="/home/$(whoami)/download/bitcoin.update"
# bitcoinOSversion
if [ "$(uname -m | grep -c 'arm')" -gt 0 ]; then
bitcoinOSversion="arm-linux-gnueabihf"
elif [ "$(uname -m | grep -c 'aarch64')" -gt 0 ]; then
bitcoinOSversion="aarch64-linux-gnu"
elif [ "$(uname -m | grep -c 'x86_64')" -gt 0 ]; then
bitcoinOSversion="x86_64-linux-gnu"
fi
# installed version
installedVersion=$(sudo -u bitcoin /usr/local/bin/bitcoind --version | head -n1 | cut -d" " -f4 | cut -c 2-)
# test if the installed version already the tested/recommended update version
bitcoinUpdateInstalled=$(echo "${installedVersion}" | grep -c "${bitcoinVersion}")
# get latest release from GitHub releases
bitcoinLatestVersion=$(curl --header "X-GitHub-Api-Version:2022-11-28" -s https://api.github.com/repos/bitcoin/bitcoin/releases | jq -r '.[].tag_name' | sort | tail -n1 | cut -c 2-)
# INFO
function displayInfo() {
echo "# basic data"
echo "installedVersion='${installedVersion}'"
echo "bitcoinOSversion='${bitcoinOSversion}'"
echo "# the tested/recommended update option"
echo "bitcoinUpdateInstalled='${bitcoinUpdateInstalled}'"
echo "bitcoinVersion='${bitcoinVersion}'"
echo "# reckless update option (latest Bitcoin Core release from GitHub)"
echo "bitcoinLatestVersion='${bitcoinLatestVersion}'"
}
if [ "${mode}" = "info" ]; then
displayInfo
exit 1
fi
# tested
if [ "${mode}" = "tested" ]; then
echo "# bitcoin.update.sh tested"
elif [ "${mode}" = "reckless" ]; then
# RECKLESS
# this mode is just for people running test and development nodes - its not recommended
# for production nodes. In a update/recovery scenario it will not install a fixed version
# it will always pick the latest release from the github
echo "# bitcoin.update.sh reckless"
bitcoinVersion=${bitcoinLatestVersion}
pathVersion=${bitcoinVersion}
elif [ "${mode}" = "custom" ]; then
if [ $# -gt 1 ]; then
bitcoinVersion="$2"
else
clear
echo
echo "# Update Bitcoin Core to a chosen version."
echo
echo "# Input the version you would like to install and press ENTER."
echo "# Examples (versions below 22.1 are not supported):"
echo "24.0.1"
echo "26.0"
echo
read bitcoinVersion
fi
if [ $(echo ${bitcoinVersion} | grep -c "rc") -gt 0 ]; then
cutVersion=$(echo ${bitcoinVersion} | awk -F"r" '{print $1}')
rcVersion=$(echo ${bitcoinVersion} | awk -F"r" '{print $2}')
# https://bitcoincore.org/bin/bitcoin-core-22.0/test.rc3/
pathVersion=${cutVersion}/test.r${rcVersion}
else
pathVersion=${bitcoinVersion}
fi
if curl --output /dev/null --silent --head --fail \
https://bitcoincore.org/bin/bitcoin-core-${pathVersion}/SHA256SUMS.asc; then
echo "# OK version exists at https://bitcoincore.org/bin/bitcoin-core-${pathVersion}"
if [ "${mode}" = "custom" ] && [ "$3" = "skipverify" ]; then
echo "# skipping signature verification"
fi
echo "# Press ENTER to proceed to install Bitcoin Core $bitcoinVersion or CTRL+C to abort."
read key
else
echo "# FAIL $bitcoinVersion does not exist"
echo
echo "# Press ENTER to return to the main menu"
read key
exit 0
fi
fi
# JOINED INSTALL
if [ "${mode}" = "tested" ] || [ "${mode}" = "reckless" ] || [ "${mode}" = "custom" ]; then
displayInfo
if [ "$installedVersion" = "$bitcoinVersion" ]; then
echo "# installedVersion = bitcoinVersion"
echo "# exiting script"
exit 0
fi
echo
echo "# clean & change into download directory"
sudo rm -rf "${downloadDir}"
mkdir -p "${downloadDir}"
cd "${downloadDir}" || exit 1
echo "# Receive signer keys"
curl -s "https://api.github.com/repos/bitcoin-core/guix.sigs/contents/builder-keys" |
jq -r '.[].download_url' | while read url; do curl -s "$url" | gpg --import; done
# download signed binary sha256 hash sum file
wget --prefer-family=ipv4 --progress=bar:force -O SHA256SUMS https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS
# download the signed binary sha256 hash sum file and check
wget --prefer-family=ipv4 --progress=bar:force -O SHA256SUMS.asc https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS.asc
if [ "${mode}" = "custom" ] && [ "$3" = "skipverify" ]; then
echo "# skipping signature verification"
echo "# display the output of 'gpg --verify SHA256SUMS.asc'"
gpg --verify SHA256SUMS.asc
else
if gpg --verify SHA256SUMS.asc; then
echo
echo "****************************************"
echo "OK --> BITCOIN MANIFEST IS CORRECT"
echo "****************************************"
echo
else
echo
echo "# BUILD FAILED --> the PGP verification failed"
echo "# try again or with a different version"
echo "# if you want to skip verifying all signatures (and just show them) use the command:"
echo "# /home/admin/config.scripts/bonus.bitcoin.sh custom ${bitcoinVersion:-<version>} skipverify"
exit 1
fi
fi
echo "# Downloading Bitcoin Core v${bitcoinVersion} for ${bitcoinOSversion} ..."
binaryName="bitcoin-${bitcoinVersion}-${bitcoinOSversion}.tar.gz"
wget https://bitcoincore.org/bin/bitcoin-core-${pathVersion}/${binaryName}
if [ ! -f "./${binaryName}" ]; then
echo "# FAIL # Downloading BITCOIN BINARY did not succeed."
exit 1
fi
echo "# Checking the binary checksum ..."
if ! sha256sum -c --ignore-missing SHA256SUMS; then
# get the sha256 value for the corresponding platform from signed hash sum file
bitcoinSHA256=$(grep -i "${binaryName}}" SHA256SUMS | cut -d " " -f1)
echo "# FAIL # Downloaded BITCOIN BINARY CHECKSUM:"
echo "$(sha256sum ${binaryName})"
echo "NOT matching SHA256 checksum:"
echo "${bitcoinSHA256}"
exit 1
else
echo
echo "# OK --> VERIFIED BITCOIN CORE BINARY CHECKSUM IS CORRECT"
echo
fi
fi
# JOINED INSTALL
if [ "${mode}" = "tested" ] || [ "${mode}" = "reckless" ] || [ "${mode}" = "custom" ]; then
# install
echo "# Stopping bitcoind ..."
sudo systemctl stop bitcoind 2>/dev/null
sudo systemctl stop tbitcoind 2>/dev/null
sudo systemctl stop sbitcoind 2>/dev/null
echo
echo "# Installing Bitcoin Core v${bitcoinVersion}"
tar -xvf ${binaryName}
sudo install -m 0755 -o root -g root -t /usr/local/bin/ bitcoin-${bitcoinVersion}/bin/*
sleep 3
if ! sudo /usr/local/bin/bitcoind --version | grep "${bitcoinVersion}"; then
echo
echo "# BUILD FAILED --> Was not able to install bitcoind version(${bitcoinVersion})"
exit 1
fi
echo "# OK Bitcoin Core ${bitcoinVersion} is installed"
exit 0
else
echo "# error='parameter not known'"
exit 1
fi

View file

@ -21,3 +21,7 @@ echo "***********************************************" >> $logFile
# make sure SSH server is configured & running
sudo /home/joinmarket/standalone/ssh.sh checkrepair >> ${logFile}
# fix ownership of possibly migrated files
sudo chown -R bitcoin:bitcoin /home/bitcoin/
sudo chown -R joinmarket:joinmarket /home/joinmarket/

View file

@ -0,0 +1,296 @@
#!/bin/bash
# https://i2pd.readthedocs.io
if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "-help" ]; then
echo "I2P Daemon install script"
echo "More info at https://i2pd.readthedocs.io"
echo "Usage:"
echo "install.i2pd.sh install -> Install i2pd"
echo "install.i2pd.sh on -> Switch on i2pd"
echo "install.i2pd.sh off -> Uninstall i2pd"
echo "install.i2pd.sh addseednodes -> Add 21 randonly selected I2P seed nodes from: https://github.com/bitcoin/bitcoin/blob/master/contrib/seeds/nodes_main.txt"
echo "install.i2pd.sh status -> I2P related logs from bitcoind, bitcoin-cli -netinfo 4 and webconsole access"
exit 1
fi
function confAdd {
# get parameters
keystr="$1"
valuestr=$(echo "$2" | sed 's/\//\\\//g')
configFile="$3"
# check if key needs to be added (prepare new entry)
entryExists=$(grep -c "^${keystr}=" ${configFile})
if [ ${entryExists} -eq 0 ]; then
echo "${keystr}=" | sudo tee -a ${configFile} 1>/dev/null
fi
# add an extra key=value line (needs sudo to operate when user is not root)
echo "${keystr}=${valuestr}" | sudo tee -a ${configFile}
}
function add_repo {
# Add repo for the latest version
# i2pd — https://repo.i2pd.xyz/.help/readme.txt
# https://repo.i2pd.xyz/.help/add_repo
source /etc/os-release
DIST=$ID
case $ID in
debian | ubuntu | raspbian)
if [[ -n $DEBIAN_CODENAME ]]; then
VERSION_CODENAME=$DEBIAN_CODENAME
fi
if [[ -n $UBUNTU_CODENAME ]]; then
VERSION_CODENAME=$UBUNTU_CODENAME
fi
if [[ -z $VERSION_CODENAME ]]; then
echo "Couldn't find VERSION_CODENAME in your /etc/os-release file. Did your system supported? Please report issue to me by writing to email: 'r4sas <at> i2pd.xyz'"
exit 1
fi
RELEASE=$VERSION_CODENAME
;;
*)
if [[ -z $ID_LIKE || "$ID_LIKE" != "debian" && "$ID_LIKE" != "ubuntu" ]]; then
echo "Your system is not supported by this script. Currently it supports debian-like and ubuntu-like systems."
exit 1
else
DIST=$ID_LIKE
case $ID_LIKE in
debian)
if [[ "$ID" == "kali" ]]; then
if [[ "$VERSION" == "2019"* || "$VERSION" == "2020"* ]]; then
RELEASE="buster"
elif [[ "$VERSION" == "2021"* || "$VERSION" == "2022"* ]]; then
RELEASE="bullseye"
fi
else
RELEASE=$DEBIAN_CODENAME
fi
;;
ubuntu)
RELEASE=$UBUNTU_CODENAME
;;
esac
fi
;;
esac
if [[ -z $RELEASE ]]; then
echo "Couldn't detect your system release. Please report issue to me by writing to email: 'r4sas <at> i2pd.xyz'"
exit 1
fi
echo "Importing signing key"
wget -q -O - https://repo.i2pd.xyz/r4sas.gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/i2pd.gpg add -
echo "Adding APT repository"
echo "deb https://repo.i2pd.xyz/$DIST $RELEASE main" | sudo tee /etc/apt/sources.list.d/i2pd.list
echo "deb-src https://repo.i2pd.xyz/$DIST $RELEASE main" | sudo tee -a /etc/apt/sources.list.d/i2pd.list
}
function bitcoinI2Pstatus {
echo "# I2P related logs from the bitcoin debug log"
echo "sudo tail -n 200 ${bitcoinLogPath} | grep i2p"
echo
sudo cat ${bitcoinLogPath} | grep i2p
echo
echo "# Running the command:"
echo "sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4"
echo
sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4
echo
echo "# i2pd webconsole:"
localip=$(hostname -I | awk '{print $1}')
echo "http://${localip}:7070"
echo "# Username: i2pd"
echo "# Password: your passwordB"
echo
}
echo "# Running: 'install.i2pd.sh $*'"
source /home/joinmarket/joinin.conf
bitcoinConfPath="/home/bitcoin/.bitcoin/bitcoin.conf"
bitcoinLogPath="/home/bitcoin/.bitcoin/debug.log"
# make sure to be present in PATH
if ! echo "$PATH" | grep "/usr/sbin" >/dev/null; then
export PATH=$PATH:/usr/sbin
echo "PATH=\$PATH:/usr/sbin" | sudo tee -a /etc/profile
fi
if [ "$1" = "install" ]; then
isInstalled=$(sudo systemctl list-unit-files | grep -c i2pd)
if [ "${isInstalled}" != "0" ]; then
echo "# i2pd is already installed."
else
echo "# Installing i2pd ..."
add_repo
sudo apt-get update
sudo apt-get install -y i2pd
fi
exit 0
fi
if [ "$1" = "1" ] || [ "$1" = "on" ]; then
isInstalled=$(sudo systemctl list-unit-files | grep -c i2pd)
if [ "${isInstalled}" != "0" ]; then
echo "# i2pd is installed."
else
/home/joinmarket/standalone/install.i2pd.sh install
fi
if systemctl is-active --quiet i2pd.service; then
echo "# i2pd.service is already active."
else
echo "# sudo systemctl enable i2pd"
sudo systemctl enable i2pd
fi
echo "# i2pd config"
/home/joinmarket/set.value.sh set debug tor ${bitcoinConfPath} noquotes
confAdd debug i2p ${bitcoinConfPath}
/home/joinmarket/set.value.sh set i2psam 127.0.0.1:7656 ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh set i2pacceptincoming 1 ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh set onlynet onion ${bitcoinConfPath} noquotes
confAdd onlynet i2p ${bitcoinConfPath}
PASSWORD_B=$(sudo cat ${bitcoinConfPath} | grep rpcpassword | cut -c 13-)
cat <<EOF | sudo tee /etc/i2pd/i2pd.conf
# i2pd settings for the Joininbox
# for the defaults see:
# https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf
# Docs:
# https://i2pd.readthedocs.io/en/latest/user-guide/configuration/
loglevel = none
[http]
address=0.0.0.0
strictheaders = false
port = 7070
auth = true
user = i2pd
pass = ${PASSWORD_B}
[httpproxy]
enabled = false
[socksproxy]
enabled = false
[sam]
enabled = true
[bob]
enabled = false
[i2cp]
enabled = false
[i2pcontrol]
enabled = false
[upnp]
enabled = false
EOF
sudo ufw allow 7070 comment "i2pd-webconsole"
echo "# Start i2pd ..."
sudo systemctl start i2pd
echo "# Restart bitcoind ..."
sudo systemctl restart bitcoind 2>/dev/null
sleep 10
if i2pd --version; then
echo "# Installed i2pd"
else
echo "# i2pd is not installed"
exit 1
fi
# setting value in raspiblitz.conf
/home/joinmarket/set.value.sh set i2pd "on"
localip=$(hostname -I | awk '{print $1}')
echo "# Config: /etc/i2pd/i2pd.conf"
echo "# i2pd web console: ${localip}:7070"
echo "# Monitor i2p in bitcoind:"
echo "sudo tail -n 100 ${bitcoinLogPath} | grep i2p"
echo "sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4"
exit 0
fi
if [ "$1" = "addseednodes" ]; then
if ! sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4 | grep i2p; then
echo "# i2pd is not running"
/home/joinmarket/standalone/install.i2pd.sh on
fi
echo "Add 21 randomly selected I2P seed nodes from: https://github.com/bitcoin/bitcoin/blob/master/contrib/seeds/nodes_main.txt"
echo "Monitor in a new terminal with:"
echo "watch sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4"
echo "This will take some time ..."
# Fetch and filter the list of seed nodes
i2pSeedNodeList=$(curl -sS https://raw.githubusercontent.com/bitcoin/bitcoin/master/contrib/seeds/nodes_main.txt | grep .b32.i2p:0)
# Shuffle the list and pick the first 21 nodes
selectedNodes=$(echo "$i2pSeedNodeList" | shuf | head -n 21)
# Add each selected node
for i2pSeedNode in ${selectedNodes}; do
echo "# Add i2p seed node: ${i2pSeedNode} by running:"
echo "bitcoin-cli addnode $i2pSeedNode onetry"
sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli addnode "$i2pSeedNode" "onetry"
done
echo
echo "# Display sudo tail -n 100 ${bitcoinLogPath} | grep i2p"
sudo tail -n 100 ${bitcoinLogPath} | grep i2p
echo
echo "# Display bitcoin-cli -netinfo 4"
sudo -u bitcoin /home/bitcoin/bitcoin/bitcoin-cli -netinfo 4
exit 0
fi
# switch off
if [ "$1" = "0" ] || [ "$1" = "off" ]; then
echo "# stop & remove systemd service"
sudo systemctl stop i2pd 2>/dev/null
sudo systemctl disable i2pd.service
echo "# Uninstall with apt"
sudo apt remove -y i2pd
echo "# Remove settings from bitcoind"
/home/joinmarket/set.value.sh delete debug ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh set debug tor ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh delete i2psam ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh delete i2pacceptincoming ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh delete onlynet ${bitcoinConfPath} noquotes
/home/joinmarket/set.value.sh set onlynet onion ${bitcoinConfPath} noquotes
sudo rm /etc/systemd/system/i2pd.service
sudo ufw delete allow 7070
if ! i2pd --version 2>/dev/null; then
echo "# OK - i2pd is not installed now"
else
echo "# i2pd is still installed"
exit 1
fi
# setting value in raspiblitz.conf
/home/joinmarket/set.value.sh set i2pd "off"
exit 0
fi
if [ "$1" = "status" ]; then
bitcoinI2Pstatus
exit 0
fi
echo "# FAIL - Unknown Parameter $1"
exit 1

View file

@ -1,3 +1,5 @@
#!/bin/bash
source /home/joinmarket/_functions.sh
# add default value to config if needed

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python3
import configparser
import os
import sys
import getopt
def main(argv):
description = "\nBoltzmann - https://code.samourai.io/oxt/boltzmann/\n\
A python script computing the entropy of Bitcoin transactions and the linkability of their inputs and outputs.\n\
For a description of the metrics and related discussions:\n\
Bitcoin Transactions & Privacy (part 1) : https://gist.github.com/LaurentMT/e758767ca4038ac40aaf\n\
Bitcoin Transactions & Privacy (part 2) : https://gist.github.com/LaurentMT/d361bca6dc52868573a2\n\
Bitcoin Transactions & Privacy (part 3) : https://gist.github.com/LaurentMT/e8644d5bc903f02613c6\n\
\n\
WARNING: this feature is highly experimental and not optimised to be used with JoinMarket."
os.system('clear')
print(description)
txids = ''
try:
opts, args = getopt.getopt(argv,"t:",["txids="])
except getopt.GetoptError:
print('python run.boltzmann.py --txids=TXID1,TXID2')
sys.exit(2)
for opt, arg in opts:
if opt in ('-t', '--txids'):
txids = arg
config = configparser.ConfigParser(strict=False)
config.read('/home/joinmarket/.joinmarket/joinmarket.cfg')
rpc_user = config['BLOCKCHAIN']['rpc_user']
rpc_password = config['BLOCKCHAIN']['rpc_password']
rpc_host = config['BLOCKCHAIN']['rpc_host']
rpc_port = config['BLOCKCHAIN']['rpc_port']
tor = ''
if rpc_host.find('.onion') >= 0:
print('# Connecting to bitcoind RPC over Tor')
tor = 'torsocks'
boltzmannpath = "/home/joinmarket/boltzmann/"
bvenv = ". "+boltzmannpath+"bvenv/bin/activate"
boltzmann = tor+" python "+boltzmannpath+"boltzmann/ludwig.py -p -x 30 \
--txids="+txids
run = bvenv+";"+"export BOLTZMANN_RPC_USERNAME="+rpc_user+";\
export BOLTZMANN_RPC_PASSWORD="+rpc_password+";\
export BOLTZMANN_RPC_HOST="+rpc_host+";\
export BOLTZMANN_RPC_PORT="+rpc_port+";"+boltzmann+" 2>/dev/null"
print('# Exporting RPC connection details')
print('# Running the command:\n'+boltzmann+'\n')
os.system(run)
if __name__ == "__main__":
main(sys.argv[1:])

View file

@ -2,7 +2,7 @@
echo "# starting JoininBox ..."
if [ ! -f /home/joinmarket/joinin.conf ];then
if [ ! -f /home/joinmarket/joinin.conf ]; then
touch /home/joinmarket/joinin.conf
fi
@ -12,45 +12,44 @@ source /home/joinmarket/_functions.sh
# FIRST RUN #
#############
setupStepEntry=$(grep -c "setupStep" < $joininConfPath)
if [ "$setupStepEntry" -eq 0 ];then
echo "setupStep=0" >> $joininConfPath
setupStepEntry=$(grep -c "setupStep" <$joininConfPath)
if [ "$setupStepEntry" -eq 0 ]; then
echo "setupStep=0" >>$joininConfPath
fi
source /home/joinmarket/joinin.conf
if [ "$setupStep" -lt 100 ];then
if [ "$setupStep" -lt 5 ];then
if [ "$setupStep" -lt 100 ]; then
if [ "$setupStep" -lt 5 ]; then
# identify running env
runningEnvEntry=$(grep -c "runningEnv" < $joininConfPath)
if [ "$runningEnvEntry" -eq 0 ];then
if [ -f "/mnt/hdd/raspiblitz.conf" ];then
runningEnvEntry=$(grep -c "runningEnv" <$joininConfPath)
if [ "$runningEnvEntry" -eq 0 ]; then
if [ -f "/mnt/hdd/raspiblitz.conf" ] || [ -f "/mnt/hdd/app-data/raspiblitz.conf" ] ; then
runningEnv="raspiblitz"
elif [ -f "/usr/share/mynode/mynode_config.sh" ];then
elif [ -f "/usr/share/mynode/mynode_config.sh" ]; then
runningEnv="mynode"
else
runningEnv="standalone"
fi
echo "runningEnv=$runningEnv" >> $joininConfPath
sed -i "s#setupStep=.*#setupStep=1#g" $joininConfPath
echo "runningEnv=$runningEnv" >>$joininConfPath
sed -i "s#setupStep=.*#setupStep=1#g" $joininConfPath
fi
echo "# running in the environment: $runningEnv"
# identify cpu architecture
cpuEntry=$(grep -c "cpu" < $joininConfPath)
if [ "$cpuEntry" -eq 0 ];then
cpuEntry=$(grep -c "cpu" <$joininConfPath)
if [ "$cpuEntry" -eq 0 ]; then
cpu=$(uname -m)
echo "cpu=$cpu" >> $joininConfPath
sed -i "s#setupStep=.*#setupStep=2#g" $joininConfPath
echo "cpu=$cpu" >>$joininConfPath
sed -i "s#setupStep=.*#setupStep=2#g" $joininConfPath
fi
echo "# cpu=${cpu}"
# check Tor
torEntry=$(grep -c "runBehindTor" < $joininConfPath)
if [ "$torEntry" -eq 0 ];then
torEntry=$(grep -c "runBehindTor" <$joininConfPath)
if [ "$torEntry" -eq 0 ]; then
torTest=$(curl --socks5 localhost:9050 --socks5-hostname localhost:9050 -s \
https://check.torproject.org/ | cat | grep -m 1 Congratulations | xargs)
if [ "$torTest" = "Congratulations. This browser is configured to use Tor." ]
then
https://check.torproject.org/ | cat | grep -m 1 Congratulations | xargs)
if [ "$torTest" = "Congratulations. This browser is configured to use Tor." ]; then
runBehindTor=on
else
runBehindTor=off
@ -59,65 +58,65 @@ if [ "$setupStep" -lt 100 ];then
echo "# Press ENTER to continue without Tor or CTRL+C to cancel and try checking again with 'menu'"
read key
fi
echo "runBehindTor=$runBehindTor" >> $joininConfPath
echo "runBehindTor=$runBehindTor" >>$joininConfPath
echo "# runBehindTor=$runBehindTor"
fi
# make sure Tor path is known
DirEntry=$(grep -c "HiddenServiceDir" < $joininConfPath)
if [ "$DirEntry" -eq 0 ];then
if [ -d "/mnt/hdd/tor" ];then
DirEntry=$(grep -c "HiddenServiceDir" <$joininConfPath)
if [ "$DirEntry" -eq 0 ]; then
if [ -d "/mnt/hdd/tor" ]; then
HiddenServiceDir="/mnt/hdd/tor"
else
HiddenServiceDir="/var/lib/tor"
fi
echo "HiddenServiceDir=$HiddenServiceDir" >> $joininConfPath
sed -i "s#setupStep=.*#setupStep=3#g" $joininConfPath
echo "HiddenServiceDir=$HiddenServiceDir" >>$joininConfPath
sed -i "s#setupStep=.*#setupStep=3#g" $joininConfPath
fi
# check for dialog
if [ "$(dialog | grep -c "ComeOn Dialog!")" -eq 0 ];then
if [ "$(dialog | grep -c "ComeOn Dialog!")" -eq 0 ]; then
sudo apt-get install -y dialog
fi
# check for qrencode
if [ "$(qrencode -V 2>&1 | grep -c "not found")" -gt 0 ];then
if [ "$(qrencode -V 2>&1 | grep -c "not found")" -gt 0 ]; then
sudo apt-get install -y qrencode
fi
sed -i "s#setupStep=.*#setupStep=4#g" $joininConfPath
sed -i "s#setupStep=.*#setupStep=4#g" $joininConfPath
# check if JoinMarket is installed
if [ "${runningEnv}" = raspiblitz ] && [ "$(lsb_release -sc)" = bullseye ]; then
# no qtgui for raspibliz on bullseye
/home/joinmarket/install.joinmarket.sh -i install -q 0
else
if [ "${cpu}" = x86_64 ]; then
/home/joinmarket/install.joinmarket.sh -i install
else
# no qtgui on arm
/home/joinmarket/install.joinmarket.sh -i install -q 0
fi
sed -i "s#setupStep=.*#setupStep=5#g" $joininConfPath
sed -i "s#setupStep=.*#setupStep=5#g" $joininConfPath
fi
# change the ssh password if standalone
if [ "$runningEnv" = "standalone" ];then
if [ "$runningEnv" = "standalone" ]; then
source /home/joinmarket/joinin.conf
if [ "$setupStep" -lt 6 ];then
if [ "$setupStep" -lt 6 ]; then
# set ssh passwords on the first run
sudo /home/joinmarket/set.password.sh
sed -i "s#setupStep=.*#setupStep=6#g" $joininConfPath
sed -i "s#setupStep=.*#setupStep=6#g" $joininConfPath
fi
source /home/joinmarket/joinin.conf
if [ "$setupStep" -lt 7 ]&&[ ${cpu} != "x86_64" ];then
if [ "$setupStep" -lt 7 ] && [ ${cpu} != "x86_64" ]; then
# expand SDcard partition on ARM
sudo /home/joinmarket/standalone/expand.rootfs.sh
fi
fi
generateJMconfig
sudo sed -i "s#setupStep=.*#setupStep=10#g" $joininConfPath
sudo sed -i "s#setupStep=.*#setupStep=10#g" $joininConfPath
source /home/joinmarket/joinin.conf
if [ "$setupStep" -lt 11 ];then
if [ "$runningEnv" = "standalone" ];then
if [ "$setupStep" -lt 11 ]; then
if [ "$runningEnv" = "standalone" ]; then
# open the config menu if standalone
/home/joinmarket/menu.config.sh
else
# setup finished
sudo sed -i "s#setupStep=.*#setupStep=100#g" $joininConfPath
sudo sed -i "s#setupStep=.*#setupStep=100#g" $joininConfPath
fi
fi
fi
@ -128,11 +127,11 @@ fi
# check bitcoind RPC setting
# add default value to joinin config if needed
if ! grep -Eq "^RPCoverTor=" $joininConfPath;then
echo "RPCoverTor=off" >> $joininConfPath
if ! grep -Eq "^RPCoverTor=" $joininConfPath; then
echo "RPCoverTor=off" >>$joininConfPath
fi
# check if bitcoin RPC connection is over Tor
if grep -Eq "^rpc_host = .*.onion" $JMcfgPath;then
if grep -Eq "^rpc_host = .*.onion" $JMcfgPath; then
echo "# RPC over Tor is on"
sed -i "s/^RPCoverTor=.*/RPCoverTor=on/g" $joininConfPath
else
@ -142,14 +141,14 @@ fi
# check if there is only one joinmarket wallet and make default
# add default value to joinin config if needed
if ! grep -Eq "^defaultWallet=" $joininConfPath;then
echo "defaultWallet=off" >> $joininConfPath
if ! grep -Eq "^defaultWallet=" $joininConfPath; then
echo "defaultWallet=off" >>$joininConfPath
fi
if [ "$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -cv /)" -gt 1 ];then
if [ "$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -cv /)" -gt 1 ]; then
echo "# Found more than one wallet file"
echo "# Setting defaultWallet to off"
sed -i "s#^defaultWallet=.*#defaultWallet=off#g" $joininConfPath
elif [ "$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -cv /)" -eq 1 ];then
elif [ "$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -cv /)" -eq 1 ]; then
onlyWallet=$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -v /)
echo "# Found only one wallet file: $onlyWallet"
echo "# Using it as default"
@ -157,28 +156,28 @@ elif [ "$(ls -p /home/joinmarket/.joinmarket/wallets/ | grep -cv /)" -eq 1 ];the
fi
# add default value to joinin config if needed
if ! grep -Eq "^network=" $joininConfPath;then
echo "network=unknown" >> $joininConfPath
if ! grep -Eq "^network=" $joininConfPath; then
echo "network=unknown" >>$joininConfPath
fi
isMainnet=$(grep -c "network = mainnet" < $JMcfgPath)
isSignet=$(grep -c "network = signet" < $JMcfgPath)
isTestnet=$(grep -c "network = testnet" < $JMcfgPath)
if [ $isMainnet -gt 0 ];then
isMainnet=$(grep -c "network = mainnet" <$JMcfgPath)
isSignet=$(grep -c "network = signet" <$JMcfgPath)
isTestnet=$(grep -c "network = testnet" <$JMcfgPath)
if [ $isMainnet -gt 0 ]; then
sed -i "s#^network=.*#network=mainnet#g" $joininConfPath
elif [ $isSignet -gt 0 ];then
elif [ $isSignet -gt 0 ]; then
sed -i "s#^network=.*#network=signet#g" $joininConfPath
elif [ $isTestnet -gt 0 ];then
elif [ $isTestnet -gt 0 ]; then
sed -i "s#^network=.*#network=testnet#g" $joininConfPath
else
sed -i "s#^network=.*#network=unknown#g" $joininConfPath
fi
# add default value to joinin config if needed
if ! grep -Eq "^localip=" $joininConfPath;then
echo "localip=unknown" >> $joininConfPath
if ! grep -Eq "^localip=" $joininConfPath; then
echo "localip=unknown" >>$joininConfPath
fi
localip=$(hostname -I | awk '{print $1}')
sed -i "s#^localip=.*#localip=$localip#g" $joininConfPath
# change the onion_serving_port if LND is present (avoid collusion with LND REST port)
# change the onion_serving_port if LND is present (avoid collision with LND REST port)
sed -i "s#^onion_serving_port = 8080#onion_serving_port = 8090#g" $JMcfgPath

View file

@ -43,6 +43,18 @@ 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

@ -15,7 +15,7 @@ fi
# Example for commits created on GitHub:
# PGPsigner="web-flow"
# PGPpubkeyLink="https://github.com/${PGPsigner}.gpg"
# PGPpubkeyFingerprint="4AEE18F83AFDEB23"
# PGPpubkeyFingerprint="B5690EEEBB952194"
# Example for commits signed with a personal PGP key:
# PGPsigner="janoside"
@ -30,9 +30,18 @@ PGPsigner="$1"
PGPpubkeyLink="$2"
PGPpubkeyFingerprint="$3"
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)
_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)
if [ "${fingerprint}" -lt 1 ]; then
echo
echo "# WARNING --> the PGP fingerprint is not as expected for ${PGPsigner}" >&2
@ -40,11 +49,9 @@ if [ "${fingerprint}" -lt 1 ]; then
echo "# Exiting" >&2
exit 7
fi
gpg --import /dev/shm/pgp_keys_${PGPsigner}.asc
rm /dev/shm/pgp_keys_${PGPsigner}.asc
gpg --quiet --import "${keyFile}"
trap 'rm -f "$_temp"' EXIT
_temp="$(mktemp -p /dev/shm/)"
_temp="${_temp_dir}/git-verify.out"
if [ $# -eq 3 ] || [ -z "$4" ]; then
commitHash="$(git log --oneline | head -1 | awk '{print $1}')"

34
test/README.md Normal file
View file

@ -0,0 +1,34 @@
# 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

@ -0,0 +1,214 @@
#!/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" = "" ]
}

11
test/run-bats-local.sh Executable file
View file

@ -0,0 +1,11 @@
#!/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,4 +7,5 @@
[default.extend-words]
# don't correct these false positives
ba = "ba"
ned = "ned"
ned = "ned"
fpr = "fpr"