Compare commits

...

52 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
openoms
c240ba2885
build: update to Debian 12.2.0, create home folders as 0755 (#139)
* build: create home folders as 0755 on Debian 12
* build: add bash
* fix: add  --shell /bin/bash  to user creation
* copy the skeleton files for users with login
* update workflows
* jam-remote update to v0.1.6
* amd64 image update to debian-12.2.0, clean vars
2023-10-23 09:28:35 +02:00
openoms
a596e25ce0
build: git verify the last commit instead of tag 2023-08-31 11:32:59 +02:00
openoms
accf274730
joinmarket update to v0.9.10 (#138) 2023-08-31 06:39:11 +00:00
openoms
709ea134dc
fix typos and add spelling (#136)
* chore: fix typos
* ci: add spelling gh action
* chore: fix false positives
2023-07-23 08:35:04 +02:00
openoms
8a924cd07b
update amd64 base image to debian 12.1.0 (#137) 2023-07-23 08:34:20 +02:00
openoms
0e388a9898
docs: update readme and screenshots (#134) 2023-06-16 14:34:57 +00:00
openoms
ec556ab64a
update bitcoin core to v25.0 (#133)
* update bitcoin core to v25.0
* fix version check
* add the joinmarket user to the bitcon group
2023-06-16 14:08:58 +00:00
openoms
b1e933d013
ci: update arm64-rpi base image (#132) 2023-06-16 14:06:12 +00:00
openoms
6032311eca
add pruned.host4coins.net/blocks + update to debian 12.0.0 (#131)
* fix: fix echo-s and format
* feat: add pruned.host4coins.net/blocks, fix data owner
* fix: check bitcoin install permission
* fix: prefer downloads over ipv4
* add hashFileSigName
* update to debian-12.0.0-amd64-netinst image
* fix the owner of the decompressed blocks
* delete old blocks and chainstates
2023-06-15 22:32:41 +02:00
openoms
83ab1e5b2a
ci: update amd64 base image to debian-11.7.0 (#125) 2023-05-07 11:43:53 +00:00
openoms
fd39fade35
fix: pin werkzeug dependency to 2.2.0 + fmt (#124) 2023-05-07 08:18:20 +00:00
63 changed files with 2362 additions and 988 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" ]
@ -26,17 +30,24 @@ jobs:
- uses: actions/checkout@v3
- name: Set values
id: set_values
run: |
echo "BUILD_DATE=$(date +"%Y-%m-%d")" >> $GITHUB_ENV
echo "BUILD_VERSION=$(git describe --always --tags)" >> $GITHUB_ENV
if [[ "${{github.event_name}}" == "pull_request" ]]; then
echo "::set-output name=github_user::${{github.event.pull_request.head.repo.owner.login}}"
else
echo "::set-output name=github_user::$(echo ${{github.repository}} | cut -d'/' -f1)"
fi
- name: Hello JoininBox
- name: Display the build name
run: echo "Building the joininbox-amd64-debian-image-${{ env.BUILD_DATE }}-${{ env.BUILD_VERSION }}"
- name: Run the build script
run: |
echo "Running with: ${{steps.set_values.outputs.github_user}} $GITHUB_HEAD_REF"
cd ci/amd64
bash packer.build.amd64-debian.sh $GITHUB_ACTOR $GITHUB_HEAD_REF
bash packer.build.amd64-debian.sh ${{steps.set_values.outputs.github_user}} $GITHUB_HEAD_REF
- name: Compute checksum of the raw image
run: |
@ -54,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" ]
@ -28,11 +32,17 @@ jobs:
- uses: actions/checkout@v3
- name: Set values
id: set_values
run: |
echo "BUILD_DATE=$(date +"%Y-%m-%d")" >> $GITHUB_ENV
echo "BUILD_VERSION=$(git describe --always --tags)" >> $GITHUB_ENV
if [[ "${{github.event_name}}" == "pull_request" ]]; then
echo "::set-output name=github_user::${{github.event.pull_request.head.repo.owner.login}}"
else
echo "::set-output name=github_user::$(echo ${{github.repository}} | cut -d'/' -f1)"
fi
- name: Hello JoininBox
- name: Display the build name
run: echo "Building the joininbox-arm64-rpi-image-${{ env.BUILD_DATE }}-${{ env.BUILD_VERSION }}"
- name: Set up QEMU
@ -42,8 +52,9 @@ jobs:
- name: Run the build script
run: |
echo "Running with: ${{steps.set_values.outputs.github_user}} $GITHUB_HEAD_REF"
cd ci/arm64-rpi
bash arm64-rpi.sh $GITHUB_ACTOR $GITHUB_HEAD_REF
bash arm64-rpi.sh ${{steps.set_values.outputs.github_user}} $GITHUB_HEAD_REF
- name: Compute checksum of the raw image
run: |
@ -61,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

17
.github/workflows/spelling.yml vendored Normal file
View file

@ -0,0 +1,17 @@
name: spelling
on:
pull_request:
branches: [ master ]
jobs:
spelling:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Spell Check Repo
uses: crate-ci/typos@master
with:
config: typos.toml

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

189
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 dekstop 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,18 +438,77 @@ 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`
* To rescan on the node run (https://developer.bitcoin.org/reference/rpc/rescanblockchain.html?highlight=rescanblockchain):
`bitcoin-cli -rpcwallet=joininbox rescanblockchain 477120`
Rescanning fom the first SegWit block is sufficient for the default SegWit wallets.
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

@ -22,6 +22,9 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
- [Features](#features)
- [Required Hardware](#required-hardware)
- [A computer running a Debian / Ubuntu Linux flavour.](#a-computer-running-a-debian--ubuntu-linux-flavour)
- [RaspberryPi 5 or 4](#raspberrypi-5-or-4)
- [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)
- [Tested environments](#tested-environments)
@ -29,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
@ -40,20 +42,26 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
* Connect remotely to a Bitcoin Core node
* 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://prunednode.today/
* JoininBox is part the RaspiBlitz SERVICES
* Start a pruned node from https://pruned.host4coins.net/blocks
* 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
* RaspberryPi 4 ([alternatively any other computer running a Debian Linux flavour](#tested-environments))
### A computer running a Debian / Ubuntu Linux flavour.
* See the [tested-environments](#tested-environments).
### RaspberryPi 5 or 4
* Power supply (5V 3A and above recommended)
* Heatsink case
* 16GB SDcard (minimum) - 32GB to use a pruned node
* 32 GB Endurance type SDcard
* [(USB SSD to run a pruned bitcoin node locally)](FAQ.md#usb-ssd-recommendation)
* [other tested hardware ](#tested-environments)
### VPS eg: [host4coins.net](https://host4coins.net/)
Recommended minimum:
* 1 GB RAM
* 1 vCPU
* 32 GB SSD
**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 used to recover the funds!**
@ -87,7 +95,7 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
* next will be presented with the CONFIG menu to
* Connect to a remote bitcoin node on mainnet
* Try JoinMarket on signet
* Start a pruned node from [prunednode.today](https://prunednode.today)
* Start a pruned node from [pruned.host4coins.net/blocks](https://pruned.host4coins.net/blocks)
* Edit the joinmarket.cfg manually
* Update JoininBox or JoinMarket
@ -105,7 +113,7 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
* Latest
* Debian Bullseye X86_64 desktop ([images and logs available in GitHub actions](https://github.com/openoms/joininbox/actions?query=workflow%3Aamd64-image-build++branch%3Amaster+is%3Asuccess))
* Raspberry Pi 4 running 64bit Debian Bullseye ([images and logs available in GitHub actions](https://github.com/openoms/joininbox/actions?query=workflow%3Aarm64-rpi-image-build++branch%3Amaster+is%3Asuccess++))
* Ubuntu 22.04 X86_64 desktop (virtual machine)
* Ubuntu 22.04 Jammy X86_64 desktop (virtual machine)
* Previous versions tested
* [Raspberry Pi 4 running 64bit Debian Buster](FAQ.md#build-the-sdcard-image)
* [Hardkernel Odroid XU4/HC1 running 32bit Armbian Buster](FAQ.md#set-up-armbian-on-the-hardkernel-odroid-xu4)
@ -114,7 +122,7 @@ A minimalistic, security focused linux environment for JoinMarket with a termina
### 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
@ -153,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

@ -5,7 +5,7 @@
# https://github.com/openoms/joininbox#tested-environments-for-joininbox
# login with SSH or boot directly
# run this script as root or with sudo
# can specify donwloading from a branch or forked repo:
# can specify downloading from a branch or forked repo:
# bash build_joininbox.sh [branch] [github user]
########################################################################
@ -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 "###################################"
@ -78,7 +78,7 @@ if [ $(cat /etc/os-release 2>/dev/null | grep -c 'Debian') -gt 0 ]; then
elif [ $(cat /etc/os-release 2>/dev/null | grep -c 'Ubuntu') -gt 0 ]; then
baseimage="ubuntu"
else
echo "\n!!! FAIL: Base Image cannot be detected or is not supported."
echo "\n# FAIL: Base Image cannot be detected or is not supported."
cat /etc/os-release 2>/dev/null
uname -a
exit 1
@ -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,70 +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 --progress=bar:force https://www.python.org/ftp/python/${pythonVersion}/Python-${pythonVersion}.tgz
# optional signature for verification
wget --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 !!!"
echo "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
@ -360,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
@ -369,7 +357,9 @@ echo "# JoininBox"
echo "#############"
echo
echo "# add the 'joinmarket' user"
adduser --disabled-password --gecos "" joinmarket
adduser --system --group --shell /bin/bash --home /home/joinmarket joinmarket
echo "Copy the skeleton files for login"
sudo -u joinmarket cp -r /etc/skel/. /home/joinmarket/
echo "# clone the joininbox repo and copy the scripts"
cd /home/joinmarket || exit 1
@ -399,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 \
${PGPsigner} ${PGPpubkeyLink} ${PGPpubkeyFingerprint} ${tag}"
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"
@ -463,7 +453,7 @@ if [ "$torTest" != "Congratulations. This browser is configured to use Tor." ];
echo "torKeyAvailable=${torKeyAvailable}"
if [ ${torKeyAvailable} -eq 0 ]; then
# https://support.torproject.org/apt/tor-deb-repo/
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
wget --prefer-family=ipv4 -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
echo "OK"
else
@ -492,13 +482,13 @@ 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
cd .. || exit 1
dpkg -i tor_*.deb
# setup Tor in the backgound
# setup Tor in the background
# TODO - test if remains in the background after the Tor service is started
tor &
else
@ -540,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
@ -592,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
@ -608,23 +598,29 @@ echo "#########################"
echo "# Download Bitcoin Core"
echo "#########################"
echo
sudo -u joinmarket /home/joinmarket/install.bitcoincore.sh downloadCoreOnly || exit 1
sudo -u joinmarket bash /home/joinmarket/install.bitcoincore.sh downloadCoreOnly || exit 1
echo
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"
sed -i "s/^qtgui=.*/qtgui=false/g" /home/joinmarket/joinin.conf
fi
sudo -u joinmarket /home/joinmarket/install.joinmarket.sh -i install -q $qtgui || exit 1
sudo -u joinmarket bash /home/joinmarket/install.joinmarket.sh -i install -q $qtgui || exit 1
echo "###################"
echo "# bootstrap.service"

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,150 +0,0 @@
{
"variables": {
"box_basename": "debian",
"build_directory": "../builds",
"build_timestamp": "{{isotime \"20060102150405\"}}",
"cpus": "2",
"disk_size": "30000",
"git_revision": "__unknown_git_revision__",
"guest_additions_url": "",
"headless": "false",
"http_directory": "{{template_dir}}/http",
"http_proxy": "{{env `http_proxy`}}",
"https_proxy": "{{env `https_proxy`}}",
"iso_checksum": "e482910626b30f9a7de9b0cc142c3d4a079fbfa96110083be1d0b473671ce08d",
"iso_name": "debian-11.6.0-amd64-netinst.iso",
"memory": "2048",
"mirror": "http://cdimage.debian.org/cdimage/release",
"mirror_directory": "current/amd64/iso-cd",
"name": "debian",
"no_proxy": "{{env `no_proxy`}}",
"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_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
"guest_additions_url": "{{ user `guest_additions_url` }}",
"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,31 +1,135 @@
#!/bin/bash -e
# Install packer
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
# Install qemu
echo -e "\nInstalling qemu..."
sudo apt-get install -y qemu-system
if [ $# -gt 0 ]; then
github_user=$1
# install packer
if ! packer version 2>/dev/null; then
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository -y "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update
echo -e "\nInstalling packer..."
sudo apt-get install -y packer
else
github_user=openoms
echo "# Packer is installed"
fi
# install qemu and UEFI firmware
echo "# Install qemu ..."
sudo apt-get update
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
else
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 -e "\nBuilding 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 = "b77cf8a9d46ce1be16a7a478c243b2197be5c217482db2bfee10fc94764c3805"
file_checksum = "62d025b9bc7ca0e1facfec74ae56ac13978b6745c58177f081d39fbb8041ed45"
file_target_extension = "xz"
file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"]
file_urls = ["https://raspi.debian.net/tested/20230101_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",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

View file

@ -1,7 +1,7 @@
<!-- omit in toc -->
# Prepare a bitcoin node to accept a remote RPC connection
If you have arrived here from runing JoininBox locally on the RaspiBlitz can skip these steps (CTRL+C to exit the connection settings). The connection to the local bitcoin node is set up automatically.
If you have arrived here from running JoininBox locally on the RaspiBlitz can skip these steps (CTRL+C to exit the connection settings). The connection to the local bitcoin node is set up automatically.
JoinMarket (running in JoininBox) needs to connect to Bitcoin Core via RPC.
A pruned node with the **bitcoind wallet enabled** will do and txindex is not required.
@ -9,13 +9,13 @@ A pruned node with the **bitcoind wallet enabled** will do and txindex is not re
- [Enable the bitcoind wallet](#enable-the-bitcoind-wallet)
- [LAN connection](#lan-connection)
- [Tor connection](#tor-connection)
- [CONFIG -> CONNECT in JoininBox](#config---connect-in-joininbox)
- [CONFIG -\> CONNECT in JoininBox](#config---connect-in-joininbox)
- [RoninDojo](#ronindojo)
- [Enable the bitcoind wallet](#enable-the-bitcoind-wallet-1)
- [LAN connection](#lan-connection-1)
- [Tor connection](#tor-connection-1)
- [Recompile the Docker containers (takes time)](#recompile-the-docker-containers-takes-time)
- [CONFIG -> CONNECT in JoininBox](#config---connect-in-joininbox-1)
- [CONFIG -\> CONNECT in JoininBox](#config---connect-in-joininbox-1)
- [Resources](#resources)
## RaspiBlitz
@ -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,15 +40,15 @@ 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` entires especially for the localhost: `127.0.0.1`
* can keep the other `rpcallowip` and `rpcbind` entries especially for the localhost: `127.0.0.1`
Example:
```bash
@ -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

@ -128,7 +128,7 @@ RaspiBlitz integration:
- pair the parallel 2nd node to Sphinx, BoS, ThunderHub, BTCPay - see: https://github.com/rootzoll/raspiblitz/issues/2073
Standalone:
- ElectRS install scritpt to be used with a local disk (requires a non-pruned node) - [FAQ](https://github.com/openoms/joininbox/blob/master/FAQ.md#external-drive)
- ElectRS install script to be used with a local disk (requires a non-pruned node) - [FAQ](https://github.com/openoms/joininbox/blob/master/FAQ.md#external-drive)
```
369c883 payjoin: extend dialog window to content
@ -263,7 +263,7 @@ b047395 (menu-improvements) getRPC values in the customRPC function
0e08c25 add XPUBS option to the WALLET menu
840094f specter: update to 1.2.2
1f60cfc add bitcoind LOGS to TOOLS
bf4672d specter insall fixes
bf4672d specter install fixes
16236e9 add SPECTER menu with UPDATE option
fa71818 add PRUNED node and LOCAL option
c3336cd implement checkRPC to create and load core wallet

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

@ -1,7 +1,7 @@
#!/bin/bash
# source aliases from /home/joinmarket/_aliases.sh
if [ -f /home/joinmarket/_aliases.sh ];then
if [ -f /home/joinmarket/_aliases.sh ]; then
source /home/joinmarket/_aliases.sh
fi
@ -14,7 +14,7 @@ function menu() {
}
# command: newnym
function newnym(){
function newnym() {
if [ "$(cat /home/joinmarket/joinin.conf 2>/dev/null | grep -c "runBehindTor=on")" -eq 1 ]; then
echo "# Changing Tor circuits..."
echo "# Savind old ID..."
@ -26,11 +26,11 @@ function newnym(){
newID=$(curl --connect-timeout 15 --socks5-hostname 127.0.0.1:9050 ifconfig.me 2>/dev/null)
echo
if [ ${oldID} = ${newID} ]; then
echo "# !!! FAIL: Identity did not change. Read error message above."
echo "# FAIL: Identity did not change. Read error message above."
echo "# Exiting for precaution."
exit 0
else
echo "# !!! SUCCESS"
echo "# SUCCESS"
echo "# Old id: " ${oldID}
echo "# New id: " ${newID}
fi
@ -41,7 +41,7 @@ function newnym(){
# command: torthistx
function torthistx() {
if [ "$(cat /home/joinmarket/joinin.conf 2>/dev/null | grep -c "runBehindTor=on")" -eq 1 ];then
if [ "$(cat /home/joinmarket/joinin.conf 2>/dev/null | grep -c "runBehindTor=on")" -eq 1 ]; then
echo
if [ "$(cat /home/joinmarket/joinin.conf 2>/dev/null | grep -c "network=signet")" -eq 1 ]; then
newnym
@ -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,11 +9,12 @@ joininConfPath="/home/joinmarket/joinin.conf"
function downloadBitcoinCore() {
# set version
# https://bitcoincore.org/en/download/
bitcoinVersion="22.0"
bitcoinVersion="29.2"
# https://github.com/laanwj
PGPname="Wladimir J. van der Laan"
PGPpubkey="74810B012346C9A6"
if bitcoin-cli --version | grep $bitcoinVersion >/dev/null; then
echo "# Bitcoin Core $bitcoinVersion is already installed"
return 0
fi
echo
echo "# *** PREPARING BITCOIN ***"
@ -21,41 +22,25 @@ function downloadBitcoinCore() {
sudo -u joinmarket mkdir /home/joinmarket/download 2>/dev/null
cd /home/joinmarket/download || exit 1
# receive signer key
if ! gpg -k | grep "${PGPpubkey}"; then
if ! gpg --keyserver hkp://keyserver.ubuntu.com --recv-key "${PGPpubkey}"; then
echo "# !!! FAIL !!! Couldn't download ${PGPname}'s PGP pubkey"
exit 1
fi
fi
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
if [ ! -f SHA256SUMS ]; then
sudo -u joinmarket wget --progress=bar:force https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS
fi
# download signed binary sha256 hash sum file and check
if [ ! -f SHA256SUMS.asc ]; then
sudo -u joinmarket wget --progress=bar:force https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS.asc
fi
verifyResult=$(gpg --verify SHA256SUMS.asc 2>&1)
goodSignature=$(echo ${verifyResult} | grep 'Good signature' -c)
echo "goodSignature(${goodSignature})"
correctKey=$(echo ${verifyResult} | tr -d " \t\n\r" | grep "${PGPpubkey}" -c)
echo "correctKey(${correctKey})"
if [ ${correctKey} -lt 1 ] || [ ${goodSignature} -lt 1 ]; then
echo
echo "# !!! BUILD FAILED --> the PGP verification failed / signature(${goodSignature}) verify(${correctKey})"
echo "# Removing the conflicting files"
echo "# This is normal after an update - try to choose the option / run the script again."
sudo rm -f SHA256SUMS
sudo rm -f SHA256SUMS.asc
exit 1
else
sudo -u joinmarket 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
sudo -u joinmarket wget --prefer-family=ipv4 --progress=bar:force -O SHA256SUMS.asc https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS.asc
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"
exit 1
fi
# detect CPU architecture & fitting download link
@ -75,10 +60,10 @@ function downloadBitcoinCore() {
# download resources
binaryName="bitcoin-${bitcoinVersion}-${bitcoinOSversion}.tar.gz"
if [ ! -f "./${binaryName}" ]; then
sudo -u joinmarket wget --progress=bar:force https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/${binaryName}
sudo -u joinmarket wget --prefer-family=ipv4 --progress=bar:force https://bitcoincore.org/bin/bitcoin-core-${bitcoinVersion}/${binaryName}
fi
if [ ! -f "./${binaryName}" ]; then
echo "!!! FAIL !!! Could not download the BITCOIN BINARY"
echo "# FAIL - Could not download the BITCOIN BINARY"
exit 1
else
# check binary checksum test
@ -89,7 +74,7 @@ function downloadBitcoinCore() {
echo "Valid SHA256 checksum should be: ${bitcoinSHA256}"
echo "Downloaded binary SHA256 checksum: ${binaryChecksum}"
if [ "${binaryChecksum}" != "${bitcoinSHA256}" ]; then
echo "!!! FAIL !!! Downloaded BITCOIN BINARY not matching SHA256 checksum: ${bitcoinSHA256}"
echo "# FAIL - Downloaded BITCOIN BINARY does not match SHA256 checksum: ${bitcoinSHA256}"
rm -v ./${binaryName}
exit 1
else
@ -98,8 +83,6 @@ function downloadBitcoinCore() {
echo "OK --> VERIFIED BITCOIN CORE BINARY CHECKSUM"
echo "********************************************"
echo
sleep 10
echo
fi
echo
echo "# Extracting to /home/joinmarket/download/bitcoin-${bitcoinVersion}"
@ -117,22 +100,22 @@ function installBitcoinCore() {
cd /home/joinmarket/download/bitcoin-${bitcoinVersion}/bin/ || exit 1
sudo install -m 0755 -o root -g root -t /home/joinmarket/bitcoin ./*
if [ "$(grep -c "/home/joinmarket/bitcoin" < /home/joinmarket/.profile)" -eq 0 ];then
if [ "$(grep -c "/home/joinmarket/bitcoin" </home/joinmarket/.profile)" -eq 0 ]; then
echo "# Add /home/joinmarket/bitcoin to the local PATH"
echo "PATH=/home/joinmarket/bitcoin:$PATH" | sudo tee -a /home/joinmarket/.profile
fi
installed=$(/home/joinmarket/bitcoin/bitcoind --version | grep -c "Bitcoin Core version")
if [ ${installed} -lt 1 ]; then
echo
echo "!!! BUILD FAILED --> Was not able to install Bitcoin Core"
echo "# BUILD FAILED --> Was not able to install Bitcoin Core"
exit 1
fi
# bitcoin.conf
if [ ! -f /home/joinmarket/.bitcoin/bitcoin.conf ]; then
mkdir -p /home/joinmarket/.bitcoin
randomRPCpass=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c8)
cat > /home/joinmarket/.bitcoin/bitcoin.conf <<EOF
randomRPCpass=$(tr </dev/urandom -dc _A-Z-a-z-0-9 | head -c8)
cat >/home/joinmarket/.bitcoin/bitcoin.conf <<EOF
# bitcoind configuration for signet
# Connection settings
@ -172,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
@ -204,8 +206,8 @@ signet.addnode=s7fcvn5rblem7tiquhhr7acjdhu7wsawcph7ck44uxyd6sismumemcyd.onion:38
signet.addnode=6megrst422lxzsqvshkqkg6z2zhunywhyrhy3ltezaeyfspfyjdzr3qd.onion:38333
signet.addnode=jahtu4veqnvjldtbyxjiibdrltqiiighauai7hmvknwxhptsb4xat4qd.onion:38333
signet.addnode=f4kwoin7kk5a5kqpni7yqe25z66ckqu6bv37sqeluon24yne5rodzkqd.onion:38333
signet.addnode=nsgyo7begau4yecc46ljfecaykyzszcseapxmtu6adrfagfrrzrlngyd.onion:38333"|\
sudo tee -a /home/joinmarket/.bitcoin/bitcoin.conf
signet.addnode=nsgyo7begau4yecc46ljfecaykyzszcseapxmtu6adrfagfrrzrlngyd.onion:38333" |
sudo tee -a /home/joinmarket/.bitcoin/bitcoin.conf
fi
# add to path
@ -245,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"
@ -263,14 +265,14 @@ setJMconfigToSignet() {
echo "# Set signet directory nodes"
# comment mainnet
sed -i \
"s/^directory_nodes = 3kxw6lf5vf6y26emzwgibzhrzhmhqiw6ekrek3nqfjjmhwznb2moonad.onion:5222,jmdirjmioywe2s5jad7ts6kgcqg66rj6wujj6q77n6wbdrgocqwexzid.onion:5222,bqlpq6ak24mwvuixixitift4yu42nxchlilrcqwk2ugn45tdclg42qid.onion:5222/\
"s/^directory_nodes = 3kxw6lf5vf6y26emzwgibzhrzhmhqiw6ekrek3nqfjjmhwznb2moonad.onion:5222,jmdirjmioywe2s5jad7ts6kgcqg66rj6wujj6q77n6wbdrgocqwexzid.onion:5222,bqlpq6ak24mwvuixixitift4yu42nxchlilrcqwk2ugn45tdclg42qid.onion:5222/\
#directory_nodes = 3kxw6lf5vf6y26emzwgibzhrzhmhqiw6ekrek3nqfjjmhwznb2moonad.onion:5222,jmdirjmioywe2s5jad7ts6kgcqg66rj6wujj6q77n6wbdrgocqwexzid.onion:5222,bqlpq6ak24mwvuixixitift4yu42nxchlilrcqwk2ugn45tdclg42qid.onion:5222/g" \
$JMcfgPath
$JMcfgPath
# uncomment signet
sed -i \
"s/^# directory_nodes = rr6f6qtleiiwic45bby4zwmiwjrj3jsbmcvutwpqxjziaydjydkk5iad.onion:5222,k74oyetjqgcamsyhlym2vgbjtvhcrbxr4iowd4nv4zk5sehw4v665jad.onion:5222,y2ruswmdbsfl4hhwwiqz4m3sx6si5fr6l3pf62d4pms2b53wmagq3eqd.onion:5222/\
"s/^# directory_nodes = rr6f6qtleiiwic45bby4zwmiwjrj3jsbmcvutwpqxjziaydjydkk5iad.onion:5222,k74oyetjqgcamsyhlym2vgbjtvhcrbxr4iowd4nv4zk5sehw4v665jad.onion:5222,y2ruswmdbsfl4hhwwiqz4m3sx6si5fr6l3pf62d4pms2b53wmagq3eqd.onion:5222/\
directory_nodes = rr6f6qtleiiwic45bby4zwmiwjrj3jsbmcvutwpqxjziaydjydkk5iad.onion:5222,k74oyetjqgcamsyhlym2vgbjtvhcrbxr4iowd4nv4zk5sehw4v665jad.onion:5222,y2ruswmdbsfl4hhwwiqz4m3sx6si5fr6l3pf62d4pms2b53wmagq3eqd.onion:5222/g" \
$JMcfgPath
$JMcfgPath
# set joinin.conf value
/home/joinmarket/set.value.sh set network signet ${joininConfPath}
@ -290,13 +292,13 @@ function showBitcoinLogs() {
elif [ "${network}" = "signet" ]; then
bitcoinUser="joinmarket"
fi
if [ "${network}" = mainnet ];then
if [ "${network}" = mainnet ]; then
logFilePath="/home/${bitcoinUser}/.bitcoin/debug.log"
elif [ "${network}" = signet ];then
elif [ "${network}" = signet ]; then
logFilePath="/home/${bitcoinUser}/.bitcoin/signet/debug.log"
fi
dialog \
--title "Monitoring the ${network} logs" \
--title "Monitoring the ${network} logs" \
--msgbox "
Will tail the bitcoin ${network} logfile from:
@ -309,30 +311,89 @@ Press CTRL+C to exit and type 'menu' for the GUI." 10 54
# getRPC - reads the RPC settings from the joinmarket.cfg
function getRPC {
echo "# Reading the bitcoind RPC settings from the joinmarket.cfg"
rpc_user="$(awk '/^rpc_user / {print $3}' < $JMcfgPath)"
rpc_pass="$(awk '/^rpc_password / {print $3}' < $JMcfgPath)"
rpc_host="$(awk '/^rpc_host / {print $3}' < $JMcfgPath)"
rpc_port="$(awk '/^rpc_port / {print $3}' < $JMcfgPath)"
rpc_wallet="$(awk '/^rpc_wallet_file / {print $3}' < $JMcfgPath)"
rpc_user="$(awk '/^rpc_user / {print $3}' <$JMcfgPath)"
rpc_pass="$(awk '/^rpc_password / {print $3}' <$JMcfgPath)"
rpc_host="$(awk '/^rpc_host / {print $3}' <$JMcfgPath)"
rpc_port="$(awk '/^rpc_port / {print $3}' <$JMcfgPath)"
rpc_wallet="$(awk '/^rpc_wallet_file / {print $3}' <$JMcfgPath)"
if [ ${#1} -gt 0 ] && [ $1 = print ]; then
echo "$rpc_user $rpc_pass $rpc_host $rpc_port $rpc_wallet"
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
if [ $# -eq 0 ];then
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 for the remote 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"
@ -341,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
@ -354,29 +415,98 @@ 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
# $1=id $2=method $3=string params $4=print
function customRPC {
getRPC
if [ ${#4} -eq 0 ];then
if [ ${#4} -eq 0 ]; then
print="no"
else
print=$4
fi
if [ $print = print ];then
echo print
fi
if [ $print = print ]; then
echo print
fi
tor=""
if [ $(echo $rpc_host | grep -c .onion) -gt 0 ]; then
tor="torsocks"
echo "# Connecting over Tor..."
echo
fi
is_int () { test "$@" -eq "$@" 2> /dev/null; }
if is_int "$3" ||[ ${#3} -eq 0 ]; then
if [ $print = print ];then
is_int() { test "$@" -eq "$@" 2>/dev/null; }
if is_int "$3" || [ ${#3} -eq 0 ]; then
if [ $print = print ]; then
echo "# Using the RPC command:"
echo "$tor curl -sS --data-binary\
'{\"jsonrpc\": \"1.0\", \"id\":\"$1\", \"method\": \"$2\", \"params\": [$3] }'\
@ -384,10 +514,10 @@ fi
echo
fi
$tor curl -sS --data-binary \
'{"jsonrpc": "1.0", "id":"'"$1"'", "method": "'"$2"'", "params": ['"$3"'] }' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
'{"jsonrpc": "1.0", "id":"'"$1"'", "method": "'"$2"'", "params": ['"$3"'] }' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
else
if [ $print = print ];then
if [ $print = print ]; then
echo "# Using the RPC command:"
echo "$tor curl -sS --data-binary\
'{\"jsonrpc\": \"1.0\", \"id\":\"$1\", \"method\": \"$2\", \"params\": [\"$3\"] }'\
@ -395,8 +525,8 @@ fi
echo
fi
$tor curl -sS --data-binary \
'{"jsonrpc": "1.0", "id":"'"$1"'", "method": "'"$2"'", "params": ["'"$3"'"] }' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
'{"jsonrpc": "1.0", "id":"'"$1"'", "method": "'"$2"'", "params": ["'"$3"'"] }' \
http://$rpc_user:$rpc_pass@$rpc_host:$rpc_port/wallet/$rpc_wallet | jq .
fi
}
@ -408,28 +538,34 @@ function connectLocalNode() {
fi
echo "# Setting connection to the local Bitcoin node on ${network}"
rpc_host="127.0.0.1"
if [ "${network}" = mainnet ];then
if [ "${network}" = mainnet ]; then
rpc_port="8332"
elif [ "${network}" = signet ];then
elif [ "${network}" = signet ]; then
rpc_port="38332"
elif [ "${network}" = testnet ];then
elif [ "${network}" = testnet ]; then
rpc_port="18332"
fi
rpc_wallet="wallet.dat"
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-)
elif [ $runningEnv = mynode ];then
rpc_wallet="watch-only-descriptor-wallet"
if [ $runningEnv = raspiblitz ]; then
if [ -f "/mnt/hdd/raspiblitz.conf" ]; then
rpc_user=$(sudo cat /mnt/hdd/bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
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)
elif [ $runningEnv = standalone ];then
rpc_user=$(sudo cat /home/bitcoin/.bitcoin/bitcoin.conf|grep rpcuser|cut -c 9-)
rpc_pass=$(sudo cat /home/bitcoin/.bitcoin/bitcoin.conf|grep rpcpassword|cut -c 13-)
elif [ $runningEnv = standalone ]; then
rpc_user=$(sudo cat /home/bitcoin/.bitcoin/bitcoin.conf | grep rpcuser | cut -c 9-)
rpc_pass=$(sudo cat /home/bitcoin/.bitcoin/bitcoin.conf | grep rpcpassword | cut -c 13-)
fi
# set.bitcoinrpc.py
python /home/joinmarket/set.bitcoinrpc.py --network=${network} \
--rpc_user="$rpc_user" --rpc_pass="$rpc_pass" --rpc_host=$rpc_host \
--rpc_port=$rpc_port --rpc_wallet=$rpc_wallet
--rpc_user="$rpc_user" --rpc_pass="$rpc_pass" --rpc_host=$rpc_host \
--rpc_port=$rpc_port --rpc_wallet=$rpc_wallet
# set joinin.conf value
/home/joinmarket/set.value.sh set network ${network} ${joininConfPath}

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
@ -66,7 +66,7 @@ It is best to reset the joinmarket.cfg after every install.
Do you want to reset the joinmarket.cfg to the defaults now?
" 12 65
# make decison
# make decision
pressed=$?
case $pressed in
0)
@ -87,7 +87,7 @@ dialog --backtitle "Connect to the local Bitcoin Core" \
--title "Connect to the local Bitcoin Core" \
--yesno "
Do you want to connect to the local Bitcoin Core on mainnet now?" 7 55
# make decison
# make decision
pressed=$?
case $pressed in
0)

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() {
@ -66,7 +74,7 @@ function passwordToFile() {
--title "Enter password" \
--insecure \
--passwordbox "Type or paste the wallet decryption password" 8 52 2> "$data"
# make decison
# make decision
pressed=$?
case $pressed in
0)
@ -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,9 +1,21 @@
#!/bin/bash
testedJMversion="v0.9.9"
PGPsigner="waxwing"
PGPpkeys="https://raw.githubusercontent.com/JoinMarket-Org/joinmarket-clientserver/master/pubkeys/AdamGibson.asc"
PGPcheck="2B6FC204D9BF332D062B461A141001A1AF77F20B"
# https://github.com/JoinMarket-Org/joinmarket-clientserver/releases
# Use tag for verification when available, otherwise use commit hash
testedJMversion="v0.9.12"
# https://github.com/JoinMarket-Org/joinmarket-clientserver/commits/master/
# Only used if testedJMversion is empty or not set
#testedJMcommit="ce32bafbb5d716bde61830f71266410249d43dbc"
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##/*}"
@ -11,17 +23,17 @@ nocolor="\033[0m"
red="\033[31m"
## see https://github.com/rootzoll/raspiblitz/blob/v1.7/build_sdcard.sh for code comments
usage(){
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:
@ -35,28 +47,31 @@ if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
usage
fi
error_msg(){ printf %s"${red}${me}: ${1}${nocolor}\n"; exit 1; }
assign_value(){
error_msg() {
printf %s"${red}${me}: ${1}${nocolor}\n"
exit 1
}
assign_value() {
case "${2}" in
--*) value="${2#--}";;
-*) value="${2#-}";;
*) value="${2}"
--*) value="${2#--}" ;;
-*) value="${2#-}" ;;
*) value="${2}" ;;
esac
case "${value}" in
0) value="false";;
1) value="true";;
0) value="false" ;;
1) value="true" ;;
esac
eval "${1}"="\"${value}\""
}
get_arg(){
get_arg() {
case "${3}" in
""|-*) error_msg "Option '${2}' requires an argument.";;
"" | -*) error_msg "Option '${2}' requires an argument." ;;
esac
assign_value "${1}" "${3}"
}
range_argument(){
range_argument() {
name="${1}"
eval var='$'"${1}"
shift
@ -71,17 +86,29 @@ range_argument(){
while :; do
case "${1}" in
-*=*) opt="${1%=*}"; arg="${1#*=}"; shift_n=1;;
-*) opt="${1}"; arg="${2}"; shift_n=2;;
*) opt="${1}"; arg="${2}"; shift_n=1;;
-*=*)
opt="${1%=*}"
arg="${1#*=}"
shift_n=1
;;
-*)
opt="${1}"
arg="${2}"
shift_n=2
;;
*)
opt="${1}"
arg="${2}"
shift_n=1
;;
esac
case "${opt}" in
-i|-i=*|--install|--install=*) get_arg install "${opt}" "${arg}";;
-v|-v=*|--version|--version=*) get_arg version "${opt}" "${arg}";;
-q|-q=*|--qtgui|--qtgui=*) get_arg qtgui "${opt}" "${arg}";;
-u|-u=*|--user|--user=*) get_arg user "${opt}" "${arg}";;
"") break;;
*) error_msg "Invalid option: ${opt}";;
-i | -i=* | --install | --install=*) get_arg install "${opt}" "${arg}" ;;
-v | -v=* | --version | --version=*) get_arg version "${opt}" "${arg}" ;;
-q | -q=* | --qtgui | --qtgui=*) get_arg qtgui "${opt}" "${arg}" ;;
-u | -u=* | --user | --user=*) get_arg user "${opt}" "${arg}" ;;
"") break ;;
*) error_msg "Invalid option: ${opt}" ;;
esac
shift "${shift_n}"
done
@ -89,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}"
@ -103,14 +133,16 @@ source /home/joinmarket/joinin.conf
# create user if not default
if [ "${user}" != "joinmarket" ]; then
echo "# add the '${user}' user"
sudo adduser --disabled-password --gecos "" ${user}
sudo adduser --system --group --shell /bin/bash --home /home/${user} ${user}
echo "Copy the skeleton files for login"
sudo -u ${user} cp -r /etc/skel/. /home/${user}/
sudo adduser ${user} sudo
# add user to Tor group
sudo usermod -a -G debian-tor ${user}
# configure for usage without password entry
checkEntry=$(sudo cat /etc/sudoers | grep -c "${user} ALL=(ALL) NOPASSWD:ALL")
if [ ${checkEntry} -eq 0 ]; then
echo "${user} ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers
echo "${user} ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers
fi
# Autostart
checkEntry=$(sudo -u ${user} cat /home/${user}/.bashrc | grep -c "jmvenv/bin/activate")
@ -118,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
@ -130,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"
@ -169,65 +192,39 @@ 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 -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
# Use specific python version, if set
python_args=""
if [[ ! -z "${JM_PYTHON}" ]]; then
if [[ -n "${JM_PYTHON}" ]]; then
python_args="--python=${JM_PYTHON}"
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
if [ "${qtgui}" = "false" ]; then
GUIchoice="--without-qt"
@ -241,8 +238,10 @@ function installJoinMarket() {
else
sudo -u ${user} ./install.sh "${GUIchoice}" "$python_args" || exit 1
fi
currentJMversion=$(cd /home/${user}/joinmarket-clientserver 2>/dev/null; \
git describe --tags 2>/dev/null)
currentJMversion=$(
cd /home/${user}/joinmarket-clientserver 2>/dev/null
git describe --tags 2>/dev/null
)
echo
echo "# installed JoinMarket $currentJMversion"
echo
@ -256,9 +255,9 @@ if [ "$install" = "config" ]; then
fi
# show info
dialog \
--title "Configure JoinMarket" \
--exit-label "Continue to edit the joinmarket.cfg" \
--textbox "/home/joinmarket/info.conf.txt" 43 108
--title "Configure JoinMarket" \
--exit-label "Continue to edit the joinmarket.cfg" \
--textbox "/home/joinmarket/info.conf.txt" 43 108
# edit joinmarket.cfg
/home/${user}/set.conf.sh $JMcfgPath
exit 0
@ -266,7 +265,7 @@ fi
if [ "$install" = "install" ]; then
# install joinmarket
if [ ! -f "/home/${user}/joinmarket-clientserver/jmvenv/bin/activate" ] ; then
if [ ! -f "/home/${user}/joinmarket-clientserver/jmvenv/bin/activate" ]; then
echo
echo "# JoinMarket is not yet installed - proceeding now"
echo
@ -274,10 +273,10 @@ if [ "$install" = "install" ]; then
errorOnInstall $?
echo "# Check for optional dependencies: matplotlib and scipy"
activateJMvenv
if [ "$(pip list | grep -c matplotlib)" -eq 0 ];then
if [ "$(pip list | grep -c matplotlib)" -eq 0 ]; then
pip install matplotlib
fi
if [ "$(pip list | grep -c scipy)" -eq 0 ];then
if [ "$(pip list | grep -c scipy)" -eq 0 ]; then
# https://stackoverflow.com/questions/7496547/does-python-scipy-need-blas
sudo apt-get install -y gfortran libopenblas-dev liblapack-dev
# fix 'No space left on device' with 2GB RAM

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 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

@ -55,9 +55,9 @@ if [ "$1" = "1" ] || [ "$1" = "on" ]; then
# download
mkdir download
cd download || exit 1
wget -O node-$VERSION-$DISTRO.tar.xz https://nodejs.org/dist/$VERSION/node-$VERSION-$DISTRO.tar.xz
wget --prefer-family=ipv4 -O node-$VERSION-$DISTRO.tar.xz https://nodejs.org/dist/$VERSION/node-$VERSION-$DISTRO.tar.xz
# checksum
wget -O SHASUMS256.txt https://nodejs.org/dist/$VERSION/SHASUMS256.txt
wget --prefer-family=ipv4 -O SHASUMS256.txt https://nodejs.org/dist/$VERSION/SHASUMS256.txt
if ! sha256sum -c SHASUMS256.txt --ignore-missing; then
echo "FAIL: The checksum of node-$VERSION-$DISTRO.tar.xz is not found in the SHASUMS256.txt"
rm -f node-$VERSION-$DISTRO.tar.xz*

View file

@ -3,15 +3,15 @@
# https://github.com/joinmarket-webui/jam
USERNAME=jam
WEBUI_VERSION=0.1.5
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
@ -46,7 +46,7 @@ if [ "$1" = "on" ]; then
echo "# Creating the ${USERNAME} user"
echo
sudo adduser --disabled-password --gecos "" ${USERNAME}
sudo adduser --system --group --home /home/${USERNAME} ${USERNAME}
# install nodeJS
bash ${SOURCEDIR}/bonus.nodejs.sh on
@ -57,10 +57,10 @@ if [ "$1" = "on" ]; then
sudo -u $USERNAME git clone https://github.com/$REPO
cd jam || exit 1
sudo -u $USERNAME git reset --hard v${WEBUI_VERSION}
sudo -u $USERNAME git reset --hard ${WEBUI_VERSION}
sudo -u $USERNAME bash ${SOURCEDIR}/../verify.git.sh \
"${PGPsigner}" "${PGPpubkeyLink}" "${PGPpubkeyFingerprint}" "v${WEBUI_VERSION}" || exit 1
"${PGPsigner}" "${PGPpubkeyLink}" "${PGPpubkeyFingerprint}" "${WEBUI_VERSION}" || exit 1
cd $HOME_DIR || exit 1
sudo -u $USERNAME mv jam $APP_DIR
@ -134,10 +134,15 @@ if [ "$1" = "update" ]; then
echo "FAIL - npm install did not run correctly, aborting"
exit 1
fi
sudo -u $USERNAME npm run build
echo "*** JAM UPDATED to $version ***"
fi
if ! sudo -u $USERNAME npm install; then
echo "FAIL - npm install did not run correctly, aborting"
exit 1
fi
sudo -u $USERNAME npm run build
else
echo "*** JAM NOT INSTALLED ***"
fi

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
@ -25,7 +29,7 @@ if [ "$runningEnv" = "standalone" ] && [ "$setupStep" -lt 100 ]; then
# BASIC MENU INFO
HEIGHT=16
WIDTH=64
WIDTH=68
CHOICE_HEIGHT=24
TITLE="Startup options"
MENU="
@ -38,7 +42,7 @@ if [ "$runningEnv" = "standalone" ] && [ "$setupStep" -lt 100 ]; then
OPTIONS+=(
CONNECT "Connect to a remote bitcoin node on mainnet"
SIGNET "Start on signet with a local Bitcoin Core"
PRUNED "Start a pruned node from prunednode.today")
PRUNED "Start a pruned node from pruned.host4coins.net/blocks")
if [ -f /home/bitcoin/.bitcoin/bitcoin.conf ];then
OPTIONS+=(
LOCAL "Connect to the local Bitcoin Core on mainnet")
@ -54,7 +58,7 @@ if [ "$runningEnv" = "standalone" ] && [ "$setupStep" -lt 100 ]; then
else
# BASIC MENU INFO
HEIGHT=12
WIDTH=64
WIDTH=68
CHOICE_HEIGHT=20
TITLE="Configuration options"
MENU=""
@ -70,7 +74,7 @@ else
SIGNET "Switch to signet with a local Bitcoin Core")
if [ "${runningEnv}" = standalone ]; then
OPTIONS+=(
PRUNED "Start a pruned node locally from prunednode.today")
PRUNED "Start a pruned node from pruned.host4coins.net/blocks")
HEIGHT=$((HEIGHT+1))
CHOICE_HEIGHT=$((CHOICE_HEIGHT+1))
fi

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

@ -47,7 +47,7 @@ $(echo $(cat $wallet) | sed "s#$walletPath##g")
mixdepth: $(cat $mixdepth)
" 12 55
# make decison
# make decision
pressed=$?
case $pressed in
0)
@ -141,7 +141,7 @@ mixdepth: $(cat "$mixdepth")
Miner fee: $txfeeMessage
" 16 55
# make decison
# make decision
pressed=$?
case $pressed in
0)

View file

@ -133,7 +133,7 @@ Paste the destination address" 19 69 2> "$address"
openMenuIfCancelled $?
# changeAddress
trap 'rm -f "$changeAdress"' EXIT
trap 'rm -f "$changeAddress"' EXIT
changeAddress=$(mktemp -p /dev/shm/)
if [ "$amountsats" != "0 sats" ]; then
dialog --backtitle "Custom change address" \
@ -185,7 +185,7 @@ $(cat "$address")
change address (optional):
$changeAddressMessage" 17 69
# make decison
# make decision
pressed=$?
case $pressed in
0)

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

@ -1,17 +1,19 @@
#!/bin/bash
function addUserStore() {
if [ ! -d /home/store/app-data ];then
if [ ! -d /home/store/app-data ]; then
echo "# Adding the user: store"
sudo adduser --disabled-password --gecos "" store
sudo adduser --system --group --home /home/store store
sudo -u store mkdir /home/store/app-data
echo "# Add the joinmarket user to the store group"
sudo usermod -aG store joinmarket
else
echo "# The folder /home/store/app-data is present already"
fi
}
function moveSignetData() {
if [ -f /etc/systemd/system/signetd.service ];then
if [ -f /etc/systemd/system/signetd.service ]; then
sudo systemctl stop signetd
sudo mv /home/joinmarket/.bitcoin /home/store/app-data/
sudo ln -s /home/store/app-data/.bitcoin /home/bitcoin/
@ -20,7 +22,7 @@ function moveSignetData() {
fi
}
function makeEncryptedFolder(){
function makeEncryptedFolder() {
# TODO
# make encrypted file
@ -33,32 +35,46 @@ function makeEncryptedFolder(){
}
function downloadSnapShot() {
if [ $# -eq 0 ] || [ "$1" = "pruned.host4coins.net" ]; then
hashFileName="sha256sum.txt"
hashFileSigName="sha256sum.txt.asc"
downloadDomain="pruned.host4coins.net/blocks"
pgpKeyLink="https://keys.openpgp.org/vks/v1/by-fingerprint/440C15769D19E6908CC1DDB23070DE9772DB8A48"
elif [ "$1" = "prunednode.today" ]; then
hashFileName="latest.signed.txt"
downloadDomain="prunednode.today"
pgpKeyLink="https://stepansnigirev.com/ss-specter-release.asc"
fi
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
hashFileName="latest.signed.txt"
sudo rm $hashFileName
wget https://prunednode.today/$hashFileName || exit 1
downloadFileName=$(grep .zip < $hashFileName | awk '{print $2}')
downloadLink="https://prunednode.today/$downloadFileName"
echo "# Import the PGP keys of Stepan Snigirev"
curl https://stepansnigirev.com/ss-specter-release.asc | gpg --import || exit 1
sudo rm $hashFileName 2>/dev/null
echo "# Downloading $hashFileName ..."
wget --prefer-family=ipv4 -O $hashFileName https://$downloadDomain/$hashFileName || exit 1
downloadFileName=$(grep .zip <$hashFileName | awk '{print $2}')
downloadLink="https://$downloadDomain/$downloadFileName"
echo "# Import the signing key"
curl -sS "$pgpKeyLink" | gpg --import || exit 1
echo "# Verifying the signature of the hash ..."
verifyResult=$(gpg --verify $hashFileName 2>&1)
goodSignature=$(echo ${verifyResult} | grep -c 'Good signature')
echo "# goodSignature (${goodSignature})"
if [ ${goodSignature} -eq 0 ];then
if [ ${#hashFileSigName} -gt 0 ]; then
wget --prefer-family=ipv4 -O $hashFileSigName https://$downloadDomain/$hashFileSigName || exit 1
fi
if ! gpg --verify $hashFileSigName $hashFileName; then
echo "# Invalid signature on $hashFileName"
echo "# Press ENTER to remove the invalid file or CTRL+C to abort."
read key
@ -67,18 +83,18 @@ function downloadSnapShot() {
exit 1
fi
if [ ! -f $downloadFileName ];then
if [ ! -f $downloadFileName ]; then
echo
echo "# Downloading $downloadLink ..."
echo
wget $downloadLink
wget --prefer-family=ipv4 -O $downloadFileName $downloadLink || exit 1
fi
echo "# Verifying the hash (takes time) ..."
verifyHash=$(sha256sum -c $hashFileName 2>&1)
goodHash=$(echo ${verifyHash} | grep -c OK)
echo "# goodHash ($goodHash)"
if [ ${goodSignature} -lt 1 ] || [ ${goodHash} -lt 1 ]; then
if sha256sum -c $hashFileName --ignore-missing; then
echo
echo "# The PGP signature and the hash of the downloaded snapshot is correct"
else
echo
echo "# Download failed --> the PGP signature did not match / signature(${goodSignature})"
echo "# Press ENTER to remove the invalid files or CTRL+C to abort."
@ -87,71 +103,82 @@ function downloadSnapShot() {
rm -f $hashFileName
rm -f $downloadFileName
exit 1
else
echo
echo "# The PGP signature and the hash of the downloaded snapshot is correct"
fi
echo "# Exracting to /home/store/app-data/.bitcoin ..."
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
sudo mkdir -p /home/store/app-data/.bitcoin
echo "# Make sure bitcoind is not running"
sudo systemctl stop bitcoind
if [ -f /home/bitcoin/.bitcoin/bitcoin.conf ];then
echo "# Back up bitcoin.conf"
sudo -u bitcoin mv /home/bitcoin/.bitcoin/bitcoin.conf \
/home/bitcoin/.bitcoin/bitcoin.conf.backup
fi
echo "# Check unzip"
sudo apt-get install -y unzip
sudo unzip -o $downloadFileName -d /home/store/app-data/.bitcoin
if [ -f /home/bitcoin/.bitcoin/bitcoin.conf.backup ];then
echo "# Restore bitcoin.conf"
sudo -u bitcoin mv -f /home/bitcoin/.bitcoin/bitcoin.conf.backup \
/home/bitcoin/.bitcoin/bitcoin.conf
if [ ! -d /home/store/app-data/.bitcoin ]; then
sudo mkdir -p /home/store/app-data/.bitcoin
fi
echo "# Making sure user: bitcoin exists"
sudo adduser --disabled-password --gecos "" bitcoin
sudo adduser --system --group --shell /bin/bash --home /home/bitcoin bitcoin
echo "Copy the skeleton files for login"
sudo -u bitcoin cp -r /etc/skel/. /home/bitcoin/
sudo chown -R bitcoin:bitcoin /home/store/app-data/.bitcoin
echo "# Add the joinmarket user to the bitcoin group"
sudo usermod -aG bitcoin joinmarket
echo "# Make sure bitcoind is not running"
sudo systemctl stop bitcoind
if sudo -u bitcoin ls /home/bitcoin/.bitcoin/bitcoin.conf; then
echo "# Back up bitcoin.conf"
sudo -u bitcoin mv /home/bitcoin/.bitcoin/bitcoin.conf \
/home/bitcoin/.bitcoin/bitcoin.conf.backup
fi
echo "# Clean old blocks and chainstate"
sudo rm -rf /home/store/app-data/.bitcoin/blocks
sudo rm -rf /home/store/app-data/.bitcoin/chainstate
echo "# Unzip ..."
sudo apt-get install -y unzip
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 \
/home/bitcoin/.bitcoin/bitcoin.conf
fi
}
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"
sudo adduser --disabled-password --gecos "" bitcoin
sudo adduser --system --group --shell /bin/bash --home /home/bitcoin bitcoin
echo "Copy the skeleton files for login"
sudo -u bitcoin cp -r /etc/skel/. /home/bitcoin/
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
installed=$(/home/bitcoin/bitcoin/bitcoind --version | grep -c "Bitcoin Core version")
if [ ${installed} -lt 1 ]; 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)"
echo "# BUILD FAILED --> Was not able to install Bitcoin Core)"
exit 1
fi
# bitcoin.conf
if [ -f /home/store/app-data/.bitcoin/bitcoin.conf ];then
if [ $(grep -c rpcpassword < /home/store/app-data/.bitcoin/bitcoin.conf) -eq 0 ];then
if [ -f /home/store/app-data/.bitcoin/bitcoin.conf ]; then
if [ $(sudo -u bitcoin grep -c rpcpassword </home/store/app-data/.bitcoin/bitcoin.conf) -eq 0 ]; then
echo "# Removing old bitcoin.conf without rpcpassword configured"
sudo rm /home/store/app-data/.bitcoin/bitcoin.conf
fi
fi
@ -161,14 +188,14 @@ function installBitcoinCoreStandalone() {
sudo mkdir -p /home/store/app-data/.bitcoin
sudo ln -s /home/store/app-data/.bitcoin /home/bitcoin/
if [ ! -f /home/bitcoin/.bitcoin/bitcoin.conf ];then
randomRPCpass=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c20)
if [ ! -f /home/bitcoin/.bitcoin/bitcoin.conf ]; then
randomRPCpass=$(tr </dev/urandom -dc _A-Z-a-z-0-9 | head -c20)
echo "
# bitcoind configuration for mainnet
# Connection settings
rpcuser=joininbox
rpcpassword=$randomRPCpass
rpcpassword="$randomRPCpass"
prune=1000
server=1
@ -192,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
@ -219,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
@ -229,14 +285,14 @@ WantedBy=multi-user.target
sudo systemctl start bitcoind
echo
echo "# Installed $(/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

@ -149,7 +149,7 @@ if [ "$1" = "1" ] || [ "$1" = "on" ]; then
addUserStore
sudo adduser --disabled-password --gecos "" specter
sudo adduser --system --group --home /home/specter specter
# store data with the store user
sudo mkdir -p /home/store/app-data/.specter 2>/dev/null

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 -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

11
typos.toml Normal file
View file

@ -0,0 +1,11 @@
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# source "$HOME/.cargo/env"
# cargo install typos-cli
# typos -c typos.toml
# typos -c typos.toml -w
[default.extend-words]
# don't correct these false positives
ba = "ba"
ned = "ned"
fpr = "fpr"