geco91
6 years ago
committed by
GitHub
63 changed files with 22428 additions and 9157 deletions
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2018 The RaspiBlitz developers |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
@ -0,0 +1,150 @@ |
|||
# Background: Blockchain Download |
|||
|
|||
## Why do we need to download the blockchain and not syncing it? |
|||
|
|||
The RaspiBlitz is powered by the RaspberryPi. The processing power of this SingleBoardComputer is too low to make a fast sync of the blockchain from the bitcoin peer to peer network during setup process (validation). To sync and index the complete blockchain could take weeks or even longer. Thats why the RaspiBlitz needs to download a prepared blockchain from another source. |
|||
|
|||
## Is downloading the blockchain secure? |
|||
|
|||
The downloaded Blockchain is pre-indexed and pre-validated. That is secure enough because if the user gets a "manipulated" blockchain it would not work after setup. The beginning of the downloaded blockchain needs to fit the genesis block (in bitcoind software) and the end of the downloaded blockchain needs not match with the rest of the bitcoin network state - hashes of new block distrubuted within the peer-2-peer network need to match the downloaded blockchain head. So if you downloaded a manipulated blockchain it would simply just dont work in practice. |
|||
|
|||
There might be theoretical scenarios why it would be bad for the whole network if anybody is downloading a prepared blockchain and not syncing and self-validating every block, but with a lot of self-validating full nodes already out there, just putting some RaspiBlitz with a downloaded blockchain into the mix and runnig as a self-valifating full node from that point on, is practically just strengthening the the overall network. And dont forget, the most important part of running a full node is to validate the new blocks summited by the miners - to balance out there power and to keep the Bitcoin network running by our rules. |
|||
|
|||
If you have any link to a more detailed discussion of this topic, please add it here for people to do their own research. |
|||
|
|||
## Blockchain Data |
|||
|
|||
The RaspiBlitz needs the following files from a bitcoind (>=0.17.0) that is fully sync and has indexing switched on (txindex=1) - all files from the directories: |
|||
``` |
|||
/blocks |
|||
/chainstate |
|||
/indexes |
|||
``` |
|||
|
|||
Make sure not to add other files like wallet data or lock files to a prepared download. |
|||
|
|||
You might want to include the testnet data also - then add the testnet3 folder that just contains the same three folders from the testnet blockchain. |
|||
|
|||
On download all those files need to be placed finally in the /mnt/hdd/bitcoin folder of the RaspiBlitz. |
|||
|
|||
## Download Process |
|||
|
|||
At the moment the RaspiBlitz offers two technical ways to download the blockchain: |
|||
|
|||
### FTP Download (fallback) |
|||
|
|||
The easiest way is to put the blockchain data on a FTP server and let people download it. FTP is able to download complete directories - HTTP can just handle single file. FTP clients and server hosting is widly available. |
|||
|
|||
The downside that this is a central point solution and is hard to scale up (without spending a lot of money). But it is available as a backup, if other solutions do not work. |
|||
|
|||
### Torrent Download (default) |
|||
|
|||
The preferred way is to to download the blockchain data thru the peer2peer torrent network. This way the community can help to seed the torrents (at least while downloading). Supporters of the project can setup constant seeding. There is no single point of failure within the download structure. |
|||
|
|||
In the beginning we used just on torrent file - containing all the directories and data like mentioned above. But this had the downside, that everytime when we update the torrent the seeding is bad in the beginning and downloads are slow. Good seeding needs time to build up. |
|||
|
|||
Thats why there are two torrent files now: |
|||
|
|||
#### Base Torrent File |
|||
|
|||
Inspired by the website getbitcoinblockchain.com we use one of their base torrent files to have a basic set of blocks - that will not change for the future. This torrent contains most of the data (the big file) and we dont need to change the torrent for a long time. This way the torrent can get establish a wide spread seeding and the torrent network can take the heavy load. |
|||
|
|||
At the moment this is just the blk and rev files up to the number: |
|||
- /blocks : 01385 |
|||
- /testnet3/blocks: 00152 |
|||
|
|||
#### Update Torrent File (Description) |
|||
|
|||
All the rest of the files get packaged into a second torrent file. This file will be updated much more often. The seeding is expected to be not that good and download may be slower, but thats OK because its a much smaller file. |
|||
|
|||
This way a good balance between good seeding and up-to-date blockchain can be reached. |
|||
|
|||
#### Update Torrent File (Creation) |
|||
|
|||
To create the Update Torrent file, follow the following step ... |
|||
|
|||
Have a almost 100% synced bitcoind MAINNET with txindex=1 on a RaspiBlitz |
|||
|
|||
Stop bitcoind with: |
|||
``` |
|||
sudo systemctl stop bitcoind |
|||
``` |
|||
|
|||
Delete base torrent blk-files with: |
|||
``` |
|||
sudo rm /mnt/hdd/bitcoin/blocks/blk00*.dat |
|||
sudo rm /mnt/hdd/bitcoin/blocks/blk0{1000..1390}.dat |
|||
``` |
|||
|
|||
Delete base torrent rev-files with: |
|||
``` |
|||
sudo rm /mnt/hdd/bitcoin/blocks/rev00*.dat |
|||
sudo rm /mnt/hdd/bitcoin/blocks/rev0{1000..1390}.dat |
|||
``` |
|||
|
|||
Now change to your computer where you package the torrent files and transfere the three directories into your torrent base directory (should be your current working directory): |
|||
``` |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/blocks ./blocks |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/chainstate ./chainstate |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/indexes ./indexes |
|||
``` |
|||
|
|||
Also have a almost 100% synced bitcoind TESTNET with txindex=1 on a RaspiBlitz |
|||
|
|||
Stop bitcoind with: |
|||
``` |
|||
sudo systemctl stop bitcoind |
|||
``` |
|||
|
|||
Delete base torrent blk-files with: |
|||
``` |
|||
sudo rm /mnt/hdd/bitcoin/testnet3/blocks/blk000*.dat |
|||
sudo rm /mnt/hdd/bitcoin/testnet3/blocks/blk00{100..152}.dat |
|||
``` |
|||
|
|||
Delete base torrent rev-files with: |
|||
``` |
|||
sudo rm /mnt/hdd/bitcoin/testnet3/blocks/rev000*.dat |
|||
sudo rm /mnt/hdd/bitcoin/testnet3/blocks/rev00{100..152}.dat |
|||
``` |
|||
|
|||
Now change again to your computer where you package the torrent files and transfere the three directories into your torrent base directory (should be your current working directory): |
|||
``` |
|||
mkdir testnet3 |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/testnet3/blocks ./testnet3/blocks |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/testnet3/chainstate ./testnet3/chainstate |
|||
scp -r bitcoin@[RaspiBlitzIP]:/mnt/hdd/bitcoin/testnet3/indexes ./testnet3/indexes |
|||
``` |
|||
|
|||
(Re-)name the "torrent base directory" to the same name as the torrent UPDATE file itself later (without the .torrent ending). For the correct naming see the chapter "Torrent Files Naming Schema" below. |
|||
|
|||
Now open your torrent client (e.g. qTorrent for OSX) and create a new torrent-file with the freshly renamed "torrent base directory" as source directory. |
|||
|
|||
Add this list of trackers to your torrent and start seeding (keep a free/empty line between the three single trackers): |
|||
``` |
|||
udp://tracker.justseed.it:1337 |
|||
|
|||
udp://tracker.coppersurfer.tk:6969/announce |
|||
|
|||
udp://open.demonii.si:1337/announce |
|||
|
|||
udp://denis.stalker.upeer.me:6969/announce |
|||
``` |
|||
|
|||
Name the new torrent file |
|||
|
|||
To create the torrent file can take some time. Finally add the generated torrent file to the /home.admin/assets/ of the github and change the name of the updateTorrent varibale file in the script 50torrentHDD.bitcoin.sh |
|||
|
|||
#### Torrent Files Naming Schema |
|||
|
|||
The base torrent file should always have the following naming scheme: |
|||
|
|||
`raspiblitz-[CHAINNETWORK][BASEITERATIONNUMBER]-[YEAR]-[MONTH]-[DAY]-base.torrent` |
|||
|
|||
So for example the second version of the base torrent for litecoin created on 2018-10-31 would have this name: raspiblitz-litecoin2-2018-10-31-base.torrent |
|||
|
|||
The update torrentfile should always have the following naming schema: |
|||
|
|||
`raspiblitz-[CHAINNETWORK][BASEITERATIONNUMBER]-[YEAR]-[MONTH]-[DAY]-update.torrent` |
|||
|
|||
So for exmaple an update torrent created on 2018-12-24 for litecoin that is an update to the second base torrent version would have this name: raspiblitz-litecoin2-2018-12-24-update.torrent |
@ -0,0 +1,43 @@ |
|||
# Background: RaspiBlitz Settings |
|||
|
|||
## Before Version 1.0 |
|||
|
|||
The RaspiBlitz started as small collection of shell scripts to setup a bitcoin+lightning node. At this time it was not needed to have settings file. The idea was that the scripts analyse the system state and make the changes required - automatically trying to catch up. That was OK as long RaspiBlitz was just a helper to setup your Lightning node and the rest was up to you. |
|||
|
|||
Over time users that are running a RaspiBlitz expected that it can handle more complex setup and customization. Also it should be easy to update the system (exchange sd card with a newer image) and should be able to have the same configuration afterwards - keeping its state. Thats why starting from version 1.0 there will be a raspiblitz config file stored on the HDD that stores stores the config state. |
|||
|
|||
## The Config File |
|||
|
|||
The RaspiBlitz config file is stored on the HDD root: |
|||
|
|||
`/mnt/hdd/raspiblitz.conf` |
|||
|
|||
Its simple structure is: one key-value pair per line. In the end its bash-script syntax to define varibales. The RaspiBlitz shell scripts can import this file with: |
|||
|
|||
`source /mnt/hdd/raspiblitz.conf` |
|||
|
|||
After this line all the config values are available and can be worked with. I prefer to call this line in scripts explicitly and not setting this values as environment variables, because when you read as a newbie such a script, you get an idea where the config file is stored. |
|||
|
|||
## The Config Values |
|||
|
|||
So see what config parameters are available check the comments in the following script: |
|||
|
|||
`/home/admin/_bootstrap.sh` |
|||
|
|||
## Adding new Config Values |
|||
|
|||
If you extend the RaspiBlitz scripts and you have the need to add a new config key-value add it to the `/home/admin/00enforceConfig.sh` script. There is a section for default values and setting them in the config file, if they dont exist there yet. Because this script runs on every startup, you can be sure that the default value is then available to your extended script - especially if people update their system. |
|||
|
|||
## Bootstrap Service: Enforcing the Config |
|||
|
|||
On every start of the RaspiBlitz take the config file and check if the system is running as stated in the config file and when needed make changes to the system. This is done by calling this script on startup with systemd: |
|||
|
|||
`/home/admin/_bootstrap.sh` |
|||
|
|||
So if you change the config by hand or you write a script that changes the config, then simply trigger a restart the RaspiBliz. |
|||
|
|||
Having this script checking the system on every startup, the user can easily update the SD card with a fresh image and the system will automatically establish the old state. |
|||
|
|||
## What to put into the config file and what not |
|||
|
|||
All values users put into setup or setting dialogs and that is not stored on the HDD (for example in the config files of lnd or bitcoin) is a hot cadidate to put into the raspi config file. Some values make sense to get stored as a duplicate (for performance or easy of access) - but dont get to wild. |
@ -0,0 +1,422 @@ |
|||
#!/bin/bash |
|||
######################################################################### |
|||
# Build your SD card image based on: |
|||
# RASPBIAN STRETCH WITH DESKTOP (2018-06-27) |
|||
# https://www.raspberrypi.org/downloads/raspbian/ |
|||
# SHA256: 8636ab9fdd8f58a8ec7dde33b83747696d31711d17ef68267dbbcd6cfb968c24 |
|||
########################################################################## |
|||
# setup fresh SD card with image above - login per SSH and run this script: |
|||
########################################################################## |
|||
|
|||
echo "" |
|||
echo "****************************************" |
|||
echo "* RASPIBLITZ SD CARD IMAGE SETUP v0.95 *" |
|||
echo "****************************************" |
|||
echo "" |
|||
|
|||
echo "" |
|||
echo "*** CHECK BASE IMAGE ***" |
|||
|
|||
# armv7=32Bit , armv8=64Bit |
|||
echo "Check if Linux ARM based ..." |
|||
isARM=$(uname -m | grep -c 'arm') |
|||
if [ ${isARM} -eq 0 ]; then |
|||
echo "!!! FAIL !!!" |
|||
echo "Can just build on ARM Linux, not on:" |
|||
uname -m |
|||
exit 1 |
|||
fi |
|||
echo "OK running on Linux ARM architecture." |
|||
|
|||
# keep in mind thet DietPi for Raspberry is also a stripped down Raspbian |
|||
echo "Detect Base Image ..." |
|||
baseImage="?" |
|||
isDietPi=$(uname -n | grep -c 'DietPi') |
|||
isRaspbian=$(cat /etc/os-release 2>/dev/null | grep -c 'Raspbian') |
|||
if [ ${isRaspbian} -gt 0 ]; then |
|||
baseImage="raspbian" |
|||
fi |
|||
if [ ${isDietPi} -gt 0 ]; then |
|||
baseImage="dietpi" |
|||
fi |
|||
if [ "${baseImage}" = "?" ]; then |
|||
cat /etc/os-release 2>/dev/null |
|||
echo "!!! FAIL !!!" |
|||
echo "Base Image cannot be detected or is not supported." |
|||
exit 1 |
|||
else |
|||
echo "OK running ${baseImage}" |
|||
fi |
|||
|
|||
# update debian |
|||
echo "" |
|||
echo "*** UPDATE DEBIAN ***" |
|||
sudo apt-get update |
|||
sudo apt-get upgrade -f -y --allow-change-held-packages |
|||
|
|||
# special prepare when DietPi |
|||
if [ "${baseImage}" = "dietpi" ]; then |
|||
echo "" |
|||
echo "*** PREPARE DietPi ***" |
|||
echo "renaming dietpi user ti pi" |
|||
sudo usermod -l pi dietpi |
|||
echo "install pip" |
|||
sudo apt-get update |
|||
sudo apt-get remove -y fail2ban |
|||
sudo apt-get install -y build-essential |
|||
sudp apt-get install -y python-pip |
|||
fi |
|||
|
|||
# special prepare when Raspbian |
|||
if [ "${baseImage}" = "raspbian" ]; then |
|||
echo "" |
|||
echo "*** PREPARE Raspbian ***" |
|||
# do memory split (16MB) |
|||
sudo raspi-config nonint do_memory_split 16 |
|||
# set to wait until network is available on boot (0 seems to yes) |
|||
sudo raspi-config nonint do_boot_wait 0 |
|||
# set WIFI country so boot does not block |
|||
sudo raspi-config nonint do_wifi_country US |
|||
# extra: remove some big packages not needed |
|||
sudo apt-get remove -y --purge libreoffice* |
|||
sudo apt-get clean |
|||
sudo apt-get -y autoremove |
|||
fi |
|||
|
|||
echo "" |
|||
echo "*** CONFIG ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#raspi-config |
|||
|
|||
# set new default passwort for root user |
|||
echo "root:raspiblitz" | sudo chpasswd |
|||
echo "pi:raspiblitz" | sudo chpasswd |
|||
|
|||
# set Raspi to boot up automatically with user pi (for the LCD) |
|||
# https://www.raspberrypi.org/forums/viewtopic.php?t=21632 |
|||
sudo raspi-config nonint do_boot_behaviour B2 |
|||
sudo bash -c "echo '[Service]' >> /etc/systemd/system/getty@tty1.service.d/autologin.conf" |
|||
sudo bash -c "echo 'ExecStart=' >> /etc/systemd/system/getty@tty1.service.d/autologin.conf" |
|||
sudo bash -c "echo 'ExecStart=-/sbin/agetty --autologin pi --noclear %I 38400 linux' >> /etc/systemd/system/getty@tty1.service.d/autologin.conf" |
|||
|
|||
echo "" |
|||
echo "*** SOFTWARE UPDATE ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#software-update |
|||
|
|||
# installs like on RaspiBolt |
|||
sudo apt-get install -y htop git curl bash-completion jq dphys-swapfile |
|||
|
|||
echo "" |
|||
echo "*** ADDING MAIN USER admin ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-main-user-admin |
|||
# using the default password 'raspiblitz' |
|||
|
|||
sudo adduser --disabled-password --gecos "" admin |
|||
echo "admin:raspiblitz" | sudo chpasswd |
|||
sudo adduser admin sudo |
|||
sudo chsh admin -s /bin/bash |
|||
|
|||
# configure sudo for usage without password entry |
|||
echo '%sudo ALL=(ALL) NOPASSWD:ALL' | sudo EDITOR='tee -a' visudo |
|||
|
|||
echo "*** ADDING SERVICE USER bitcoin" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-the-service-user-bitcoin |
|||
|
|||
# create user and set default password for user |
|||
sudo adduser --disabled-password --gecos "" bitcoin |
|||
echo "bitcoin:raspiblitz" | sudo chpasswd |
|||
|
|||
echo "" |
|||
echo "*** SWAP FILE ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#moving-the-swap-file |
|||
# but just deactivating and deleting old (will be created alter when user adds HDD) |
|||
|
|||
sudo dphys-swapfile swapoff |
|||
sudo dphys-swapfile uninstall |
|||
|
|||
echo "" |
|||
echo "*** INCREASE OPEN FILE LIMIT ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#increase-your-open-files-limit |
|||
|
|||
sudo sed --in-place -i "56s/.*/* soft nofile 128000/" /etc/security/limits.conf |
|||
sudo bash -c "echo '* hard nofile 128000' >> /etc/security/limits.conf" |
|||
sudo bash -c "echo 'root soft nofile 128000' >> /etc/security/limits.conf" |
|||
sudo bash -c "echo 'root hard nofile 128000' >> /etc/security/limits.conf" |
|||
sudo bash -c "echo '# End of file' >> /etc/security/limits.conf" |
|||
|
|||
sudo sed --in-place -i "23s/.*/session required pam_limits.so/" /etc/pam.d/common-session |
|||
|
|||
sudo sed --in-place -i "25s/.*/session required pam_limits.so/" /etc/pam.d/common-session-noninteractive |
|||
sudo bash -c "echo '# end of pam-auth-update config' >> /etc/pam.d/common-session-noninteractive" |
|||
|
|||
echo "" |
|||
echo "*** BITCOIN ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_30_bitcoin.md#installation |
|||
|
|||
# set version (change if update is available) |
|||
bitcoinVersion="0.17.0" |
|||
laanwjPGP="01EA5486DE18A882D4C2684590C8019E36C2E964" |
|||
|
|||
# prepare directories |
|||
sudo -u admin mkdir /home/admin/download |
|||
cd /home/admin/download |
|||
|
|||
# download resources |
|||
sudo -u admin wget https://bitcoin.org/bin/bitcoin-core-${bitcoinVersion}/bitcoin-${bitcoinVersion}-arm-linux-gnueabihf.tar.gz |
|||
if [ ! -f "./bitcoin-${bitcoinVersion}-arm-linux-gnueabihf.tar.gz" ] |
|||
then |
|||
echo "!!! FAIL !!! Download BITCOIN BINARY not success." |
|||
exit 1 |
|||
fi |
|||
sudo -u admin wget https://bitcoin.org/bin/bitcoin-core-${bitcoinVersion}/SHA256SUMS.asc |
|||
if [ ! -f "./SHA256SUMS.asc" ] |
|||
then |
|||
echo "!!! FAIL !!! Download SHA256SUMS.asc not success." |
|||
exit 1 |
|||
fi |
|||
sudo -u admin wget https://bitcoin.org/laanwj-releases.asc |
|||
if [ ! -f "./laanwj-releases.asc" ] |
|||
then |
|||
echo "!!! FAIL !!! Download laanwj-releases.asc not success." |
|||
exit 1 |
|||
fi |
|||
|
|||
# test checksum |
|||
checksum=$(sha256sum --check SHA256SUMS.asc --ignore-missing 2>/dev/null | grep '.tar.gz: OK' -c) |
|||
if [ ${checksum} -lt 1 ]; then |
|||
echo "" |
|||
echo "!!! BUILD FAILED --> Bitcoin download checksum not OK" |
|||
exit 1 |
|||
fi |
|||
|
|||
# check gpg finger print |
|||
fingerprint=$(gpg ./laanwj-releases.asc 2>/dev/null | grep "${laanwjPGP}" -c) |
|||
if [ ${fingerprint} -lt 1 ]; then |
|||
echo "" |
|||
echo "!!! BUILD FAILED --> Bitcoin download PGP author not OK" |
|||
exit 1 |
|||
fi |
|||
gpg --import ./laanwj-releases.asc |
|||
verifyResult=$(gpg --verify SHA256SUMS.asc 2>&1) |
|||
goodSignature=$(echo ${verifyResult} | grep 'Good signature' -c) |
|||
echo "goodSignature(${goodSignature})" |
|||
correctKey=$(echo ${verifyResult} | grep "using RSA key ${laanwjPGP: -16}" -c) |
|||
echo "correctKey(${correctKey})" |
|||
if [ ${correctKey} -lt 1 ] || [ ${goodSignature} -lt 1 ]; then |
|||
echo "" |
|||
echo "!!! BUILD FAILED --> LND PGP Verify not OK / signatute(${goodSignature}) verify(${correctKey})" |
|||
exit 1 |
|||
fi |
|||
|
|||
# install |
|||
sudo -u admin tar -xvf bitcoin-${bitcoinVersion}-arm-linux-gnueabihf.tar.gz |
|||
sudo install -m 0755 -o root -g root -t /usr/local/bin bitcoin-${bitcoinVersion}/bin/* |
|||
sleep 3 |
|||
installed=$(sudo -u admin bitcoind --version | grep "${bitcoinVersion}" -c) |
|||
if [ ${installed} -lt 1 ]; then |
|||
echo "" |
|||
echo "!!! BUILD FAILED --> Was not able to install bitcoind version(${bitcoinVersion})" |
|||
exit 1 |
|||
fi |
|||
|
|||
echo "" |
|||
echo "*** LITECOIN ***" |
|||
# based on https://medium.com/@jason.hcwong/litecoin-lightning-with-raspberry-pi-3-c3b931a82347 |
|||
|
|||
# set version (change if update is available) |
|||
litecoinVersion="0.16.3" |
|||
cd /home/admin/download |
|||
sudo -u admin wget https://download.litecoin.org/litecoin-${litecoinVersion}/linux/litecoin-${litecoinVersion}-arm-linux-gnueabihf.tar.gz |
|||
sudo -u admin tar -xvf litecoin-${litecoinVersion}-arm-linux-gnueabihf.tar.gz |
|||
sudo install -m 0755 -o root -g root -t /usr/local/bin litecoin-${litecoinVersion}/bin/* |
|||
installed=$(sudo -u admin litecoind --version | grep "${litecoinVersion}" -c) |
|||
if [ ${installed} -lt 1 ]; then |
|||
echo "" |
|||
echo "!!! BUILD FAILED --> Was not able to install litecoind version(${litecoinVersion})" |
|||
exit 1 |
|||
fi |
|||
|
|||
echo "" |
|||
echo "*** LND ***" |
|||
|
|||
## based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_40_lnd.md#lightning-lnd |
|||
#lndVersion="0.5-beta-rc1" |
|||
#olaoluwaPGP="65317176B6857F98834EDBE8964EA263DD637C21" |
|||
# |
|||
# get LND resources |
|||
#cd /home/admin/download |
|||
#sudo -u admin wget https://github.com/lightningnetwork/lnd/releases/download/v${lndVersion}/lnd-linux-arm-v${lndVersion}.tar.gz |
|||
#sudo -u admin wget https://github.com/lightningnetwork/lnd/releases/download/v${lndVersion}/manifest-v${lndVersion}.txt |
|||
#sudo -u admin wget https://github.com/lightningnetwork/lnd/releases/download/v${lndVersion}/manifest-v${lndVersion}.txt.sig |
|||
#sudo -u admin wget https://keybase.io/roasbeef/pgp_keys.asc |
|||
## test checksum |
|||
#checksum=$(sha256sum --check manifest-v${lndVersion}.txt --ignore-missing 2>/dev/null | grep '.tar.gz: OK' -c) |
|||
#if [ ${checksum} -lt 1 ]; then |
|||
# echo "" |
|||
# echo "!!! BUILD FAILED --> LND download checksum not OK" |
|||
# exit 1 |
|||
#fi |
|||
## check gpg finger print |
|||
#fingerprint=$(gpg ./pgp_keys.asc 2>/dev/null | grep "${olaoluwaPGP}" -c) |
|||
#if [ ${fingerprint} -lt 1 ]; then |
|||
# echo "" |
|||
# echo "!!! BUILD FAILED --> LND download author PGP not OK" |
|||
# exit 1 |
|||
#fi |
|||
#gpg --import ./pgp_keys.asc |
|||
#sleep 2 |
|||
#verifyResult=$(gpg --verify manifest-v${lndVersion}.txt.sig manifest-v${lndVersion}.txt 2>&1) |
|||
#goodSignature=$(echo ${verifyResult} | grep 'Good signature' -c) |
|||
#echo "goodSignature(${goodSignature})" |
|||
#correctKey=$(echo ${verifyResult} | grep "using RSA key ${olaoluwaPGP: -16}" -c) |
|||
#echo "correctKey(${correctKey})" |
|||
#if [ ${correctKey} -lt 1 ] || [ ${goodSignature} -lt 1 ]; then |
|||
# echo "" |
|||
# echo "!!! BUILD FAILED --> LND PGP Verify not OK / signatute(${goodSignature}) verify(${correctKey})" |
|||
# exit 1 |
|||
#fi |
|||
## install |
|||
#sudo -u admin tar -xzf lnd-linux-arm-v${lndVersion}.tar.gz |
|||
#sudo install -m 0755 -o root -g root -t /usr/local/bin lnd-linux-arm-v${lndVersion}/* |
|||
#sleep 3 |
|||
#installed=$(sudo -u admin lnd --version | grep "${lndVersion}" -c) |
|||
#if [ ${installed} -lt 1 ]; then |
|||
# echo "" |
|||
# echo "!!! BUILD FAILED --> Was not able to install LND version(${lndVersion})" |
|||
# exit 1 |
|||
#fi |
|||
|
|||
##### Build from Source |
|||
# To quickly catch up get latest patches if needed |
|||
repo="github.com/lightningnetwork/lnd" |
|||
commit="61e867741926bcb318432a6344b80161fabd1455" |
|||
# BUILDING LND FROM SOURCE |
|||
echo "*** Installing Go ***" |
|||
wget https://storage.googleapis.com/golang/go1.11.linux-armv6l.tar.gz |
|||
if [ ! -f "./go1.11.linux-armv6l.tar.gz" ] |
|||
then |
|||
echo "!!! FAIL !!! Download not success." |
|||
exit 1 |
|||
fi |
|||
sudo tar -C /usr/local -xzf go1.11.linux-armv6l.tar.gz |
|||
sudo rm *.gz |
|||
sudo mkdir /usr/local/gocode |
|||
sudo chmod 777 /usr/local/gocode |
|||
export GOROOT=/usr/local/go |
|||
export PATH=$PATH:$GOROOT/bin |
|||
export GOPATH=/usr/local/gocode |
|||
export PATH=$PATH:$GOPATH/bin |
|||
echo "*** Build LND from Source ***" |
|||
go get -d $repo |
|||
# make sure to always have the same code (commit) to build |
|||
# TODO: To update lnd -> change to latest commit |
|||
cd $GOPATH/src/$repo |
|||
sudo git checkout $commit |
|||
make && make install |
|||
sudo chmod 555 /usr/local/gocode/bin/lncli |
|||
sudo chmod 555 /usr/local/gocode/bin/lnd |
|||
sudo bash -c "echo 'export PATH=$PATH:/usr/local/gocode/bin/' >> /home/admin/.bashrc" |
|||
sudo bash -c "echo 'export PATH=$PATH:/usr/local/gocode/bin/' >> /home/pi/.bashrc" |
|||
sudo bash -c "echo 'export PATH=$PATH:/usr/local/gocode/bin/' >> /home/bitcoin/.bashrc" |
|||
lndVersionCheck=$(lncli --version) |
|||
echo "LND VERSION: ${lndVersionCheck}" |
|||
if [ ${#lndVersionCheck} -eq 0 ]; then |
|||
echo "FAIL - Something went wrong with building LND from source." |
|||
echo "Sometimes it may just be a connection issue. Reset to fresh Rasbian and try again?" |
|||
exit 1 |
|||
fi |
|||
echo "" |
|||
echo "** Link to /usr/local/bin ***" |
|||
sudo ln -s /usr/local/gocode/bin/lncli /usr/local/bin/lncli |
|||
sudo ln -s /usr/local/gocode/bin/lnd /usr/local/bin/lnd |
|||
|
|||
echo "" |
|||
echo "*** RASPIBLITZ EXTRAS ***" |
|||
|
|||
# for setup schell scripts |
|||
sudo apt-get -y install dialog bc |
|||
|
|||
# enable copy of blockchain from 2nd HDD formatted with exFAT |
|||
sudo apt-get -y install exfat-fuse |
|||
|
|||
# for blockchain torrent download |
|||
sudo apt-get -y install transmission-cli |
|||
|
|||
# for background downloading |
|||
sudo apt-get -y install screen |
|||
|
|||
# optimization for torrent download |
|||
sudo bash -c "echo 'net.core.rmem_max = 4194304' >> /etc/sysctl.conf" |
|||
sudo bash -c "echo 'net.core.wmem_max = 1048576' >> /etc/sysctl.conf" |
|||
|
|||
# *** SHELL SCRIPTS AND ASSETS |
|||
|
|||
# move files from gitclone |
|||
cd /home/admin/ |
|||
sudo -u admin git clone https://github.com/rootzoll/raspiblitz.git |
|||
sudo -u admin cp /home/admin/raspiblitz/home.admin/*.* /home/admin |
|||
sudo -u admin chmod +x *.sh |
|||
sudo -u admin cp -r /home/admin/raspiblitz/home.admin/assets /home/admin/ |
|||
|
|||
# bash aoutstart for admin |
|||
sudo bash -c "echo '# automatically start main menu for admin' >> /home/admin/.bashrc" |
|||
sudo bash -c "echo './00mainMenu.sh' >> /home/admin/.bashrc" |
|||
|
|||
# bash aoutstart for pi |
|||
# run as exec to dont allow easy physical access by keyboard |
|||
# see https://github.com/rootzoll/raspiblitz/issues/54 |
|||
sudo bash -c 'echo "# automatic start the LCD info loop" >> /home/pi/.bashrc' |
|||
sudo bash -c 'echo "SCRIPT=/home/admin/00infoLCD.sh" >> /home/pi/.bashrc' |
|||
sudo bash -c 'echo "# replace shell with script => logout when exiting script" >> /home/pi/.bashrc' |
|||
sudo bash -c 'echo "exec \$SCRIPT" >> /home/pi/.bashrc' |
|||
|
|||
# create /home/admin/setup.sh - which will get executed after reboot by autologin pi user |
|||
cat > /home/admin/setup.sh <<EOF |
|||
|
|||
# make LCD screen rotation correct |
|||
sudo sed --in-place -i "57s/.*/dtoverlay=tft35a:rotate=270/" /boot/config.txt |
|||
|
|||
EOF |
|||
sudo chmod +x /home/admin/setup.sh |
|||
|
|||
echo "" |
|||
echo "*** HARDENING ***" |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#hardening-your-pi |
|||
|
|||
# fail2ban (no config required) |
|||
sudo apt-get install -y fail2ban |
|||
|
|||
# *** BOOTSTRAP *** |
|||
# see background README for details |
|||
echo "" |
|||
echo "*** RASPI BOOSTRAP SERVICE ***" |
|||
sudo chmod +x /home/admin/_bootstrap.sh |
|||
sudo cp ./assets/bootstrap.service /etc/systemd/system/bootstrap.service |
|||
sudo systemctl enable bootstrap |
|||
|
|||
# *** RASPIBLITZ IMAGE READY *** |
|||
echo "" |
|||
echo "**********************************************" |
|||
echo "ALMOST READY" |
|||
echo "**********************************************" |
|||
echo "" |
|||
echo "Your SD Card Image for RaspiBlitz is almost ready." |
|||
echo "Last step is to install LCD drivers. This will reboot your Pi when done." |
|||
echo "" |
|||
echo "Maybe take the chance and look thru the output above if you can spot any errror." |
|||
echo "" |
|||
echo "After final reboot - your SD Card Image is ready." |
|||
echo "Press ENTER to install LCD and reboot ..." |
|||
read key |
|||
|
|||
# give Raspi a default hostname (optional) |
|||
sudo raspi-config nonint do_hostname "RaspiBlitz" |
|||
|
|||
# *** RASPIBLITZ / LCD (at last - because makes a reboot) *** |
|||
# based on https://www.elegoo.com/tutorial/Elegoo%203.5%20inch%20Touch%20Screen%20User%20Manual%20V1.00.2017.10.09.zip |
|||
cd /home/admin/ |
|||
sudo apt-mark hold raspberrypi-bootloader |
|||
git clone https://github.com/goodtft/LCD-show.git |
|||
sudo chmod -R 755 LCD-show |
|||
sudo chown -R admin:admin LCD-show |
|||
cd LCD-show/ |
|||
sudo ./LCD35-show |
@ -0,0 +1,39 @@ |
|||
#!/bin/bash |
|||
|
|||
# get raspiblitz config |
|||
source /mnt/hdd/raspiblitz.conf |
|||
|
|||
# show select dialog |
|||
CHOICES=$(dialog --checklist "Activate/Deactivate Services:" 15 40 5 \ |
|||
1 "Channel Autopilot" ${autoPilot} \ |
|||
2>&1 >/dev/tty) |
|||
#CHOICES=$(dialog --checklist "Activate/Deactivate Services:" 15 40 5 \ |
|||
#1 "Channel Autopilot" ${autoPilot} \ |
|||
#2 "UPnP Router-Portforwarding" ${natUPnP} \ |
|||
#3 "Auto Unlock on Start" ${autoUnlock} \ |
|||
#4 "Seed Torrent Blockchain" ${torrentSeed} \ |
|||
#4 "RTL Webinterface" ${rtlWebinterface} \ |
|||
#2>&1 >/dev/tty) |
|||
dialogcancel=$? |
|||
clear |
|||
|
|||
# check if user canceled dialog |
|||
if [ ${dialogcancel} -eq 1 ]; then |
|||
echo "user canceled" |
|||
exit 1 |
|||
fi |
|||
|
|||
# AUTOPILOT process choice |
|||
choice="off"; check=$(echo "${CHOICES}" | grep -c "1") |
|||
if [ ${check} -eq 1 ]; then choice="on"; fi |
|||
sudo sed -i "s/^autoPilot=.*/autoPilot=${choice}/g" /mnt/hdd/raspiblitz.conf |
|||
|
|||
# confirm reboot to activate new settings with bootstrap.service |
|||
dialog --backtitle "Rebooting" --yesno "To activate the settings a reboot is needed." 6 52 |
|||
if [ $? -eq 0 ];then |
|||
echo "Starting Reboot .." |
|||
sudo shutdown -r now |
|||
else |
|||
echo "No Reboot - changes stored, but maybe not active." |
|||
sleep 3 |
|||
fi |
@ -0,0 +1,234 @@ |
|||
#!/bin/bash |
|||
echo "" |
|||
|
|||
# see background_downloadBlockchain.md for info |
|||
# why there are two torrent files |
|||
|
|||
# torrent files that are available |
|||
# in directory /home.admin/assets/ |
|||
# WITHOUT THE '.torrent' ENDING |
|||
baseTorrentFile="raspiblitz-bitcoin1-2018-10-13-base" |
|||
updateTorrentFile="raspiblitz-bitcoin1-2018-10-13-update" |
|||
|
|||
# make sure rtorrent is available |
|||
sudo apt-get install rtorrent -y |
|||
echo "" |
|||
|
|||
targetDir="/mnt/hdd/torrent" |
|||
sessionDir="/home/admin/.rtorrent.session" |
|||
sudo mkdir ${sessionDir} 2>/dev/null |
|||
|
|||
############################## |
|||
# CHECK TORRENT 1 "BLOCKCHAIN" |
|||
############################## |
|||
|
|||
echo "*** checking torrent 1: base blockchain" |
|||
torrentComplete1=$(cat ${sessionDir}/blockchain/*.torrent.rtorrent | grep ':completei1' -c) |
|||
echo "torrentComplete1(${torrentComplete1})" |
|||
if [ ${torrentComplete1} -eq 0 ]; then |
|||
|
|||
# check if screen session for this torrent |
|||
isRunning1=$( screen -S blockchain -ls | grep "blockchain" -c ) |
|||
echo "isRunning1(${isRunning1})" |
|||
if [ ${isRunning1} -eq 0 ]; then |
|||
|
|||
# start torrent download in screen session |
|||
echo "starting torrent: blockchain" |
|||
command1="sudo rtorrent -n -d ${targetDir} -s ${sessionDir}/blockchain/ /home/admin/assets/${baseTorrentFile}.torrent" |
|||
sudo mkdir ${targetDir} 2>/dev/null |
|||
sudo mkdir ${sessionDir}/blockchain/ 2>/dev/null |
|||
screenCommand="screen -S blockchain -L screen.log -dm ${command1}" |
|||
echo "${screenCommand}" |
|||
bash -c "${screenCommand}" |
|||
fi |
|||
fi |
|||
sleep 2 |
|||
|
|||
############################## |
|||
# CHECK TORRENT 2 "UPDATE" |
|||
############################## |
|||
|
|||
echo "*** checking torrent 2: update blockchain" |
|||
torrentComplete2=$(cat ${sessionDir}/update/*.torrent.rtorrent | grep ':completei1' -c) |
|||
echo "torrentComplete2(${torrentComplete2})" |
|||
if [ ${torrentComplete2} -eq 0 ]; then |
|||
|
|||
# check if screen session for this torrent |
|||
isRunning2=$( screen -S update -ls | grep "update" -c ) |
|||
echo "isRunning2(${isRunning2})" |
|||
if [ ${isRunning2} -eq 0 ]; then |
|||
|
|||
# start torrent download in screen session |
|||
echo "starting torrent: update" |
|||
command2="sudo rtorrent -n -d ${targetDir} -s ${sessionDir}/update/ /home/admin/assets/${updateTorrentFile}.torrent" |
|||
sudo mkdir ${targetDir} 2>/dev/null |
|||
sudo mkdir ${sessionDir}/update/ 2>/dev/null |
|||
screenCommand="screen -S update -L screen.log -dm ${command2}" |
|||
echo "${screenCommand}" |
|||
bash -c "${screenCommand}" |
|||
|
|||
fi |
|||
fi |
|||
sleep 2 |
|||
|
|||
############################## |
|||
# MONITOR PROGRESS |
|||
############################## |
|||
|
|||
sleep 3 |
|||
|
|||
# monitor screen session |
|||
screenDump1="... started ..." |
|||
screenDump2="... started ..." |
|||
torrentComplete1=0 |
|||
torrentComplete2=0 |
|||
while : |
|||
do |
|||
|
|||
# display info screen |
|||
clear |
|||
echo "****************************************************" |
|||
echo "Monitoring Screen Session: Torrent base+update" |
|||
echo "If needed press key x to stop TORRENT download" |
|||
echo "NOTICE: This can take multiple hours or days !!" |
|||
echo "Its OK to close terminal now and SSH back in later." |
|||
echo "****************************************************" |
|||
echo "" |
|||
|
|||
# display torrent 1 info |
|||
echo "*** 1) Status Torrent 'blockchain':" |
|||
torrentComplete1=$(cat ${sessionDir}/blockchain/*.torrent.rtorrent | grep ':completei1' -c) |
|||
if [ ${torrentComplete1} -eq 0 ]; then |
|||
screen -S blockchain -X hardcopy .blockchain.out |
|||
newScreenDump=$(cat .blockchain.out | head -6 | tail -3 ) |
|||
if [ ${#newScreenDump} -gt 0 ]; then |
|||
screenDump1=$newScreenDump |
|||
fi |
|||
echo "$screenDump1" |
|||
else |
|||
echo "Completed" |
|||
fi |
|||
echo "" |
|||
|
|||
# display torrent 2 info |
|||
echo "*** 2) Status Torrent 'update':" |
|||
torrentComplete2=$(cat ${sessionDir}/update/*.torrent.rtorrent | grep ':completei1' -c) |
|||
if [ ${torrentComplete2} -eq 0 ]; then |
|||
screen -S update -X hardcopy .update.out |
|||
newScreenDump=$(cat .update.out| head -6 | tail -3 ) |
|||
if [ ${#newScreenDump} -gt 0 ]; then |
|||
screenDump2=$newScreenDump |
|||
fi |
|||
echo "$screenDump2" |
|||
else |
|||
echo "Completed" |
|||
fi |
|||
echo "" |
|||
|
|||
# check if both torrents completed |
|||
if [ ${torrentComplete1} -eq 1 ]; then |
|||
if [ ${torrentComplete2} -eq 1 ]; then |
|||
echo "OK - all torrents finished" |
|||
break |
|||
fi |
|||
fi |
|||
|
|||
# wait 2 seconds for key input |
|||
read -n 1 -t 2 keyPressed |
|||
|
|||
# check if user wants to abort session |
|||
if [ "${keyPressed}" = "x" ]; then |
|||
echo "" |
|||
echo "Aborting getbitcoinblockchain.com" |
|||
break |
|||
fi |
|||
|
|||
done |
|||
|
|||
# clean up |
|||
rm -f .blockchain.out |
|||
rm -f .update.out |
|||
|
|||
############################## |
|||
# AFTER PARTY & CLEAN UP |
|||
############################## |
|||
|
|||
# quit session1 |
|||
isRunning=$( screen -S blockchain -ls | grep "blockchain" -c ) |
|||
if [ ${isRunning} -eq 1 ]; then |
|||
# get the PID of screen session |
|||
sessionPID=$(screen -ls | grep "blockchain" | cut -d "." -f1 | xargs) |
|||
echo "killing screen session PID(${sessionPID})" |
|||
# kill all child processes of screen sceesion |
|||
sudo pkill -P ${sessionPID} |
|||
echo "proccesses killed" |
|||
sleep 3 |
|||
# tell the screen session to quit and wait a bit |
|||
screen -S blockchain -X quit 1>/dev/null |
|||
sleep 3 |
|||
echo "cleaning screen" |
|||
screen -wipe 1>/dev/null |
|||
sleep 3 |
|||
fi |
|||
|
|||
# quit session2 |
|||
isRunning=$( screen -S update -ls | grep "update" -c ) |
|||
if [ ${isRunning} -eq 1 ]; then |
|||
# get the PID of screen session |
|||
sessionPID=$(screen -ls | grep "update" | cut -d "." -f1 | xargs) |
|||
echo "killing screen session PID(${sessionPID})" |
|||
# kill all child processes of screen sceesion |
|||
sudo pkill -P ${sessionPID} |
|||
echo "proccesses killed" |
|||
sleep 3 |
|||
# tell the screen session to quit and wait a bit |
|||
screen -S update -X quit 1>/dev/null |
|||
sleep 3 |
|||
echo "cleaning screen" |
|||
screen -wipe 1>/dev/null |
|||
sleep 3 |
|||
fi |
|||
|
|||
# check result |
|||
torrentComplete=0 |
|||
torrentComplete1=$(cat ${sessionDir}/blockchain/*.torrent.rtorrent | grep ':completei1' -c) |
|||
torrentComplete2=$(cat ${sessionDir}/update/*.torrent.rtorrent | grep ':completei1' -c) |
|||
if [ ${torrentComplete1} -eq 1 ]; then |
|||
if [ ${torrentComplete2} -eq 1 ]; then |
|||
torrentComplete=1 |
|||
fi |
|||
fi |
|||
if [ ${torrentComplete} -eq 0 ]; then |
|||
|
|||
# User Cancel --> Torrent incomplete |
|||
sleep 3 |
|||
echo -ne '\007' |
|||
dialog --title " WARNING " --yesno "The download failed or is not complete. Maybe try again (later). Do you want keep already downloaded data for next try?" 8 57 |
|||
response=$? |
|||
case $response in |
|||
1) sudo rm -rf ${targetDir} ;; |
|||
esac |
|||
./00mainMenu.sh |
|||
exit 1; |
|||
|
|||
fi |
|||
|
|||
# the path torrent will download to |
|||
targetPath1="${targetDir}/${baseTorrentFile}" |
|||
targetPath2="${targetDir}/${updateTorrentFile}" |
|||
|
|||
# Download worked / just move, copy on USB2 >4h |
|||
echo "" |
|||
echo "*** Moving Files ***" |
|||
date +%s |
|||
echo "can take some minutes ... öease wait" |
|||
|
|||
sudo mkdir /mnt/hdd/bitcoin 2>/dev/null |
|||
sudo mv ${targetPath1}/* /mnt/hdd/bitcoin/ |
|||
sudo cp -r ${targetPath2}/* /mnt/hdd/bitcoin/ |
|||
sudo rm -r ${targetDir} |
|||
echo "OK" |
|||
date +%s |
|||
|
|||
# continue setup |
|||
./60finishHDD.sh |
@ -1,34 +1,66 @@ |
|||
# load network |
|||
network=`sudo cat /home/admin/.network` |
|||
|
|||
# load name of Blitz |
|||
name=`sudo cat /home/admin/.hostname` |
|||
|
|||
### USER PI AUTOSTART (LCD Display) |
|||
localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/') |
|||
|
|||
# parse the actual scanned height progress from LND logs |
|||
item=0 |
|||
chain="$(sudo -u bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain')" |
|||
gotData=$(sudo -u bitcoin tail -n 100 /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log | grep -c height) |
|||
if [ ${gotData} -gt 0 ]; then |
|||
item=$(sudo -u bitcoin tail -n 100 /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log | grep height | tail -n1 | awk '{print $9} {print $10} {print $11} {print $12}' | tr -dc '0-9') |
|||
fi |
|||
blockchaininfo=$(sudo -u bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo) |
|||
chain="$(echo "${blockchaininfo}" | jq -r '.chain')" |
|||
|
|||
# get total number of blocks |
|||
total=$(sudo -u bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.blocks') |
|||
## TRY to get the actual progress height of scanning |
|||
|
|||
# calculate progress in percent |
|||
percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }") |
|||
if [ ${percent} -eq 100 ]; then |
|||
# normally if 100% gets calculated, item parsed the wrong height |
|||
percent=0 |
|||
# 1) First try the "Rescanned through block" - it seems to happen if it restarts |
|||
item=$(sudo -u bitcoin tail -n 100 /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log | grep "Rescanned through block" | tail -n1 | cut -d ']' -f2 | cut -d '(' -f2 | tr -dc '0-9') |
|||
|
|||
# 2) Second try the "Caught up to height" - thats the usual on first scan start |
|||
if [ ${#item} -eq 0 ]; then |
|||
item=$(sudo -u bitcoin tail -n 100 /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log | grep "Caught up to height" | tail -n1 | cut -d ']' -f2 | tr -dc '0-9') |
|||
fi |
|||
|
|||
infoStr=$(echo " Lightning Rescanning Blockchain ${percent}%\nplease wait - this can take some time") |
|||
# TODO next fallback try later here if necessary |
|||
if [ ${#item} -eq 0 ]; then |
|||
item="?" |
|||
fi |
|||
|
|||
# get total number of blocks |
|||
total=$(echo "${blockchaininfo}" | jq -r '.blocks') |
|||
# put scanstate |
|||
scanstate="${item}/${total}" |
|||
|
|||
# get blockchain sync progress |
|||
progress="$(echo "${blockchaininfo}" | jq -r '.verificationprogress')" |
|||
|
|||
# check if blockchain is still syncing |
|||
heigh=6 |
|||
width=44 |
|||
isInitialChainSync=$(echo "${blockchaininfo}" | grep 'initialblockdownload' | grep "true" -c) |
|||
isWaitingBlockchain=$( sudo -u bitcoin tail -n 2 /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log | grep "Waiting for chain backend to finish sync" -c ) |
|||
if [ ${isWaitingBlockchain} -gt 0 ]; then |
|||
infoStr=" Waiting for final Blockchain Sync\nplease wait - this can take some time" |
|||
isInitialChainSync=1 |
|||
fi |
|||
if [ ${isInitialChainSync} -gt 0 ]; then |
|||
heigh=7 |
|||
infoStr=" Waiting for final Blockchain Sync\n Progress: ${progress}\n Please wait - this can take some time.\n ssh admin@${localip}\n Password A" |
|||
if [ "$USER" = "admin" ]; then |
|||
heigh=6 |
|||
width=53 |
|||
infoStr=$(echo " Waiting for final Blockchain Sync\n Progress: ${progress}\n Please wait - this can take some long time.\n Its OK to close terminal and ssh back in later.") |
|||
fi |
|||
else |
|||
heigh=7 |
|||
infoStr=$(echo " Lightning Rescanning Blockchain\n Progress: ${scanstate}\n Please wait - this can take some time\n ssh admin@${localip}\n Password A") |
|||
if [ "$USER" = "admin" ]; then |
|||
heigh=6 |
|||
width=53 |
|||
infoStr=$(echo " Lightning Rescanning Blockchain\n Progress: ${scanstate}\n Please wait - this can take some long time.\n Its OK to close terminal and ssh back in later.") |
|||
fi |
|||
fi |
|||
|
|||
# display progress to user |
|||
dialog --backtitle "RaspiBlitz (${localip} / ${network} / ${chain})" --infobox "${infoStr}" 4 42 |
|||
sleep 3 |
|||
dialog --title " ${network} / ${chain} " --backtitle "RaspiBlitz (${name})" --infobox "${infoStr}" ${heigh} ${width} |
@ -0,0 +1,125 @@ |
|||
#!/usr/bin/env bash |
|||
# based on pull request from vnnkl |
|||
|
|||
# load network |
|||
network=`cat .network` |
|||
|
|||
echo "" |
|||
echo "*** Switch between Testnet/Mainnet ***" |
|||
|
|||
# allow only on bitcoin network |
|||
if [ "${network}" = "bitcoin" ]; then |
|||
echo "Bitcoin network can be switched between testnet/mainnet ..." |
|||
else |
|||
echo "FAIL - Only Bitcoin Network can be switched between man/tast at the moment." |
|||
exit 1 |
|||
fi |
|||
|
|||
NETWORK_CONFIG="/home/bitcoin/.${network}/${network}.conf" |
|||
NETWORK_TEMPLATE="/home/admin/assets/${network}.conf" |
|||
LND_CONFIG="/home/bitcoin/.lnd/lnd.conf" |
|||
LND_TEMPLATE="/home/admin/assets/lnd.${network}.conf" |
|||
echo "NETWORK_CONFIG(${NETWORK_CONFIG})" |
|||
echo "LND_CONFIG(${LND_CONFIG})" |
|||
echo "NETWORK_TEMPLATE(${NETWORK_TEMPLATE})" |
|||
echo "LND_TEMPLATE(${LND_TEMPLATE})" |
|||
|
|||
# function to detect main/testnet |
|||
function isMainnet(){ |
|||
grep "^#testnet=1$" -q $NETWORK_CONFIG && return 1 |
|||
return 0 |
|||
} |
|||
|
|||
function switchToMainnet { |
|||
echo "switching to mainnet" |
|||
sed -i "s/^testnet=1/#testnet=1/g" $NETWORK_CONFIG |
|||
sed -i "s/^testnet=1/#testnet=1/g" $NETWORK_TEMPLATE |
|||
sed -i "s/^#mainnet=1/mainnet=1/g" $NETWORK_CONFIG |
|||
sed -i "s/^#mainnet=1/mainnet=1/g" $NETWORK_TEMPLATE |
|||
sed -i "s/^${network}.testnet=1/#${network}.testnet=1/g" $LND_CONFIG |
|||
sed -i "s/^#${network}.mainnet=1/${network}.mainnet=1/g" $LND_CONFIG |
|||
sed -i "s/^${network}.testnet=1/#${network}.testnet=1/g" $LND_TEMPLATE |
|||
sed -i "s/^#${network}.mainnet=1/${network}.mainnet=1/g" $LND_TEMPLATE |
|||
echo "OK switched to mainnet" |
|||
} |
|||
|
|||
function switchToTestnet { |
|||
echo "switching to testnet" |
|||
sed -i "s/^#testnet=1/testnet=1/g" $NETWORK_CONFIG |
|||
sed -i "s/^#testnet=1/testnet=1/g" $NETWORK_TEMPLATE |
|||
sed -i "s/^mainnet=1/#mainnet=1/g" $NETWORK_CONFIG |
|||
sed -i "s/^mainnet=1/#mainnet=1/g" $NETWORK_TEMPLATE |
|||
sed -i "s/^#${network}.testnet=1/${network}.testnet=1/g" $LND_CONFIG |
|||
sed -i "s/^${network}.mainnet=1/#${network}.mainnet=1/g" $LND_CONFIG |
|||
sed -i "s/^#${network}.testnet=1/${network}.testnet=1/g" $LND_TEMPLATE |
|||
sed -i "s/^${network}.mainnet=1/#${network}.mainnet=1/g" $LND_TEMPLATE |
|||
echo "OK switched to testnet" |
|||
} |
|||
|
|||
# LND Service |
|||
lndInstalled=$(systemctl status lnd.service | grep loaded -c) |
|||
if [ ${lndInstalled} -gt 0 ]; then |
|||
|
|||
echo "check for open channels" |
|||
openChannels=$(sudo -u bitcoin /usr/local/bin/lncli --chain=${network} listchannels 2>/dev/null | grep chan_id -c) |
|||
if [ ${openChannels} -gt 0 ]; then |
|||
echo "" |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL - You have still open channels and could loose funds !! - close those first with 'lncli closeallchannels' or main menu option." |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
exit 1 |
|||
else |
|||
echo "no open channels found" |
|||
fi |
|||
|
|||
echo "stopping lnd client" |
|||
systemctl stop lnd |
|||
sleep 4 |
|||
|
|||
else |
|||
echo "LND not running" |
|||
fi |
|||
|
|||
# NETWORK Service |
|||
networkInstalled=$(systemctl status ${network}d.service | grep loaded -c) |
|||
if [ ${networkInstalled} -gt 0 ]; then |
|||
echo "stopping bitcoind client" |
|||
systemctl stop bitcoind |
|||
sleep 4 |
|||
else |
|||
echo "Network ${network} not running" |
|||
fi |
|||
|
|||
# TURN THE SWITCH |
|||
isMainnet |
|||
if [ $? -eq 1 ]; then |
|||
echo "switching from mainnet to testnet" |
|||
switchToTestnet |
|||
else |
|||
echo "switching from testnet to mainnet" |
|||
switchToMainnet |
|||
fi |
|||
|
|||
echo "copying over config to admin user" |
|||
cp $NETWORK_CONFIG /home/admin/.${network}/ |
|||
chown admin:admin /home/admin/.${network}/${network}.conf |
|||
cp $LND_CONFIG /home/admin/.lnd/ |
|||
chown admin:admin /home/admin/.lnd/lnd.conf |
|||
|
|||
# restarting network |
|||
if [ ${networkInstalled} -gt 0 ]; then |
|||
|
|||
# start network |
|||
systemctl start bitcoind |
|||
echo "started ${network}d back up, giving it a 120 SECONDS to prepare" |
|||
sleep 120 |
|||
|
|||
# set setup info again |
|||
echo "60" > /home/admin/.setup |
|||
|
|||
# run again the complete LND init procedure |
|||
./70initLND.sh |
|||
|
|||
else |
|||
echo "No starting of network, because it was not running before" |
|||
fi |
@ -0,0 +1,219 @@ |
|||
#!/bin/bash |
|||
|
|||
# Background: |
|||
# https://medium.com/@lopp/how-to-run-bitcoin-as-a-tor-hidden-service-on-ubuntu-cff52d543756 |
|||
# https://bitcoin.stackexchange.com/questions/70069/how-can-i-setup-bitcoin-to-be-anonymous-with-tor |
|||
# https://github.com/lightningnetwork/lnd/blob/master/docs/configuring_tor.md |
|||
|
|||
# load network |
|||
network=`cat .network` |
|||
chain="$(${network}-cli getblockchaininfo | jq -r '.chain')" |
|||
|
|||
# location of TOR config |
|||
torrc="/etc/tor/torrc" |
|||
|
|||
# check if TOR was already installed and is funtional |
|||
clear |
|||
echo "" |
|||
echo "*** Check if TOR service is functional ***" |
|||
torRunning=$(curl --connect-timeout 10 --socks5-hostname 127.0.0.1:9050 https://check.torproject.org | grep "Congratulations. This browser is configured to use Tor." -c) |
|||
if [ ${torRunning} -gt 0 ]; then |
|||
clear |
|||
echo "You are all good - TOR is already running." |
|||
echo "" |
|||
exit 0 |
|||
else |
|||
echo "TOR not running ... proceed with switching to TOR." |
|||
echo "" |
|||
fi |
|||
|
|||
# ask user if to proceed |
|||
dialog --title " WARNING " --yesno "At the moment you just can switch TOR on - YOU CANNOT SWITCH BACK. Do you want to proceed?" 8 57 |
|||
response=$? |
|||
case $response in |
|||
1) exit 1; |
|||
esac |
|||
|
|||
echo "*** Adding Tor Sources to sources.list ***" |
|||
echo "deb http://deb.torproject.org/torproject.org stretch main" | sudo tee -a /etc/apt/sources.list |
|||
echo "deb-src http://deb.torproject.org/torproject.org stretch main" | sudo tee -a /etc/apt/sources.list |
|||
echo "OK" |
|||
echo "" |
|||
|
|||
echo "*** Installing dirmngr ***" |
|||
sudo apt install dirmngr |
|||
echo "" |
|||
|
|||
## lopp: gpg --keyserver keys.gnupg.net --recv 886DDD89 |
|||
echo "*** Fetching GPG key ***" |
|||
gpg --keyserver keys.gnupg.net --recv A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 |
|||
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add - |
|||
echo "" |
|||
|
|||
echo "*** Updating System ***" |
|||
sudo apt-get update |
|||
echo "" |
|||
|
|||
echo "*** Install Tor ***" |
|||
sudo apt install tor tor-arm -y |
|||
|
|||
echo "" |
|||
echo "*** Tor Config ***" |
|||
sudo rm -r -f /mnt/hdd/tor 2>/dev/null |
|||
sudo mkdir /mnt/hdd/tor |
|||
sudo mkdir /mnt/hdd/tor/sys |
|||
sudo mkdir /mnt/hdd/tor/web80 |
|||
sudo mkdir /mnt/hdd/tor/lnd9735 |
|||
sudo mkdir /mnt/hdd/tor/lndrpc9735 |
|||
sudo chmod -R 700 /mnt/hdd/tor |
|||
sudo chown -R bitcoin:bitcoin /mnt/hdd/tor |
|||
cat > ./torrc <<EOF |
|||
### See 'man tor', or https://www.torproject.org/docs/tor-manual.html |
|||
|
|||
DataDirectory /mnt/hdd/tor/sys |
|||
PidFile /mnt/hdd/tor/sys/tor.pid |
|||
|
|||
SafeLogging 0 |
|||
Log notice stdout |
|||
Log notice file /mnt/hdd/tor/notice.log |
|||
Log info file /mnt/hdd/tor/info.log |
|||
|
|||
RunAsDaemon 1 |
|||
User bitcoin |
|||
PortForwarding 1 |
|||
ControlPort 9051 |
|||
SocksPort 9050 |
|||
|
|||
CookieAuthFile /mnt/hdd/tor/sys/control_auth_cookie |
|||
CookieAuthentication 1 |
|||
CookieAuthFileGroupReadable 1 |
|||
|
|||
# Hidden Service v2 for WEB ADMIN INTERFACE |
|||
HiddenServiceDir /mnt/hdd/tor/web80/ |
|||
HiddenServicePort 80 127.0.0.1:80 |
|||
|
|||
# Hidden Service v2 for LND RPC |
|||
HiddenServiceDir /mnt/hdd/tor/lndrpc10009/ |
|||
HiddenServicePort 80 127.0.0.1:10009 |
|||
|
|||
# Hidden Service v3 for LND incomming connections (just in case) |
|||
# https://trac.torproject.org/projects/tor/wiki/doc/NextGenOnions#Howtosetupyourownprop224service |
|||
HiddenServiceDir /mnt/hdd/tor/lnd9735 |
|||
HiddenServiceVersion 3 |
|||
HiddenServicePort 9735 127.0.0.1:9735 |
|||
|
|||
# NOTE: bitcoind get tor service automatically - see /mnt/hdd/bitcoin for onion key |
|||
EOF |
|||
sudo rm $torrc |
|||
sudo mv ./torrc $torrc |
|||
sudo chmod 644 $torrc |
|||
sudo chown -R bitcoin:bitcoin /var/run/tor/ |
|||
echo "" |
|||
|
|||
# NYX - Tor monitor tool |
|||
# https://nyx.torproject.org/#home |
|||
echo "*** Installing NYX - TOR monitoring Tool ***" |
|||
nyxInstalled=$(sudo pip list 2>/dev/null | grep 'nyx' -c) |
|||
if [ ${nyxInstalled} -eq 0 ]; then |
|||
sudo pip install nyx |
|||
else |
|||
echo "NYX already installed" |
|||
fi |
|||
echo "" |
|||
|
|||
echo "*** Activating TOR system service ***" |
|||
echo "ReadWriteDirectories=-/mnt/hdd/tor" | sudo tee -a /lib/systemd/system/tor@default.service |
|||
sudo systemctl daemon-reload |
|||
sudo systemctl restart tor@default |
|||
echo "" |
|||
|
|||
echo "*** Waiting for TOR to boostrap ***" |
|||
torIsBootstrapped=0 |
|||
while [ ${torIsBootstrapped} -eq 0 ] |
|||
do |
|||
echo "--- Checking 1 ---" |
|||
date +%s |
|||
sudo cat /mnt/hdd/tor/notice.log 2>/dev/null | grep "Bootstrapped" | tail -n 10 |
|||
torIsBootstrapped=$(sudo cat /mnt/hdd/tor/notice.log 2>/dev/null | grep "Bootstrapped 100" -c) |
|||
echo "torIsBootstrapped(${torIsBootstrapped})" |
|||
echo "If this takes too long --> CTRL+c, reboot and check manually" |
|||
sleep 5 |
|||
done |
|||
echo "OK - Tor Bootstrap is ready" |
|||
echo "" |
|||
|
|||
echo "*** Changing ${network} Config ***" |
|||
networkIsTor=$(sudo cat /home/bitcoin/.${network}/${network}.conf | grep 'onlynet=onion' -c) |
|||
if [ ${networkIsTor} -eq 0 ]; then |
|||
|
|||
echo "Only Connect thru TOR" |
|||
echo "onlynet=onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
|
|||
if [ "${network}" = "bitcoin" ]; then |
|||
echo "Adding some bitcoin onion nodes to connect to" |
|||
echo "addnode=fno4aakpl6sg6y47.onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
echo "addnode=toguvy5upyuctudx.onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
echo "addnode=ndndword5lpb7eex.onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
echo "addnode=6m2iqgnqjxh7ulyk.onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
echo "addnode=5tuxetn7tar3q5kp.onion" | sudo tee --append /home/bitcoin/.${network}/${network}.conf |
|||
fi |
|||
|
|||
sudo cp /home/bitcoin/.${network}/${network}.conf /home/admin/.${network}/${network}.conf |
|||
sudo chown admin:admin /home/admin/.${network}/${network}.conf |
|||
|
|||
else |
|||
echo "Chain network already configured for TOR" |
|||
fi |
|||
|
|||
echo "*** ${network} re-init - Waiting for Onion Address ***" |
|||
# restarting bitcoind to start with tor and generare onion.address |
|||
echo "restarting ${network}d ..." |
|||
sudo systemctl restart ${network}d |
|||
sleep 8 |
|||
onionAddress="" |
|||
while [ ${#onionAddress} -eq 0 ] |
|||
do |
|||
echo "--- Checking 2 ---" |
|||
date +%s |
|||
testNetAdd="" |
|||
if [ "${chain}" = "test" ];then |
|||
testNetAdd="/testnet3" |
|||
fi |
|||
sudo cat /mnt/hdd/${network}${testNetAdd}/debug.log 2>/dev/null | grep "tor" | tail -n 10 |
|||
onionAddress=$(sudo -u bitcoin ${network}-cli getnetworkinfo | grep '"address"' | cut -d '"' -f4) |
|||
echo "Can take up to 20min - if this takes longer --> CTRL+c, reboot and check manually" |
|||
sleep 5 |
|||
done |
|||
onionPort=$(sudo -u bitcoin ${network}-cli getnetworkinfo | grep '"port"' | tr -dc '0-9') |
|||
echo "Your Chain Network Onion Address is: ${onionAddress}:${onionPort}" |
|||
echo "" |
|||
|
|||
echo "*** Setting your Onion Address ***" |
|||
onionLND=$(sudo cat /mnt/hdd/tor/lnd9735/hostname) |
|||
echo "Your Lightning Tor Onion Address is: ${onionLND}:9735" |
|||
echo "" |
|||
|
|||
# ACTIVATE LND OVER TOR |
|||
echo "*** Putting LND behind TOR ***" |
|||
echo "Disable LND again" |
|||
sudo systemctl disable lnd |
|||
echo "Writing Public Onion Address to /mnt/hdd/tor/v3Address (just in case for TotHiddenServiceV3)" |
|||
echo "V3ADDRESS=${onionLND}" | sudo tee /mnt/hdd/tor/v3Address |
|||
echo "Configure and Changing to lnd.tor.service" |
|||
sed -i "5s/.*/Wants=${network}d.service/" ./assets/lnd.tor.service |
|||
sed -i "6s/.*/After=${network}d.service/" ./assets/lnd.tor.service |
|||
sudo cp /home/admin/assets/lnd.tor.service /etc/systemd/system/lnd.service |
|||
sudo chmod +x /etc/systemd/system/lnd.service |
|||
echo "Enable LND again" |
|||
sudo systemctl enable lnd |
|||
echo "OK" |
|||
echo "" |
|||
|
|||
echo "*** Finshing Setup / REBOOT ***" |
|||
echo "OK - all should be set" |
|||
echo "" |
|||
echo "PRESS ENTER ... to REBOOT" |
|||
read key |
|||
|
|||
sudo shutdown -r now |
|||
exit 0 |
@ -0,0 +1,57 @@ |
|||
#!/bin/bash |
|||
|
|||
# load network |
|||
network=`cat .network` |
|||
|
|||
# location of TOR config |
|||
torrc="/etc/tor/torrc" |
|||
|
|||
echo "*** Stopping all Services ***" |
|||
sudo systemctl stop lnd |
|||
sudo systemctl stop ${network}d |
|||
sudo systemctl stop tor@default |
|||
echo "" |
|||
|
|||
echo "*** Disable TOR service ***" |
|||
sudo systemctl disable tor@default |
|||
echo "" |
|||
|
|||
echo "*** Changing ${network} Config ***" |
|||
sudo cat /home/bitcoin/.${network}/${network}.conf | grep -Ev 'onlynet=onion|.onion' | sudo tee /home/bitcoin/.${network}/${network}.conf |
|||
sudo cp /home/bitcoin/.${network}/${network}.conf /home/admin/.${network}/${network}.conf |
|||
sudo chown admin:admin /home/admin/.${network}/${network}.conf |
|||
|
|||
echo "*** Removing TOR from LND ***" |
|||
sudo systemctl disable lnd |
|||
sed -i "5s/.*/Wants=${network}d.service/" ./assets/lnd.service |
|||
sed -i "6s/.*/After=${network}d.service/" ./assets/lnd.service |
|||
sudo cp /home/admin/assets/lnd.service /etc/systemd/system/lnd.service |
|||
sudo chmod +x /etc/systemd/system/lnd.service |
|||
sudo systemctl enable lnd |
|||
echo "OK" |
|||
echo "" |
|||
|
|||
echo "*** Remove Tor ***" |
|||
sudo apt remove tor tor-arm -y |
|||
echo "" |
|||
|
|||
echo "*** Remove dirmngr ***" |
|||
sudo apt remove dirmngr -y |
|||
echo "" |
|||
|
|||
echo "*** Remove NYX ***" |
|||
sudo pip uninstall nyx -y |
|||
echo "" |
|||
|
|||
echo "*** Remove TOR Files/Config ***" |
|||
sudo rm -r -f /mnt/hdd/tor |
|||
echo "" |
|||
|
|||
echo "*** Finshing Setup / REBOOT ***" |
|||
echo "OK - all should be set" |
|||
echo "" |
|||
echo "PRESS ENTER ... to REBOOT" |
|||
read key |
|||
|
|||
sudo shutdown -r now |
|||
exit 0 |
@ -0,0 +1,22 @@ |
|||
#!/bin/bash |
|||
|
|||
# Basic Options |
|||
OPTIONS=(ZAP "Zap Wallet (iOS)" \ |
|||
SHANGO "Shango Wallet (iOS/Android)") |
|||
|
|||
CHOICE=$(dialog --clear --title "Choose Mobile Wallet" --menu "" 10 40 6 "${OPTIONS[@]}" 2>&1 >/dev/tty) |
|||
|
|||
clear |
|||
case $CHOICE in |
|||
CLOSE) |
|||
exit 1; |
|||
;; |
|||
SHANGO) |
|||
./97addMobileWalletShango.sh |
|||
exit 1; |
|||
;; |
|||
ZAP) |
|||
./97addMobileWalletZap.sh |
|||
exit 1; |
|||
;; |
|||
esac |
@ -0,0 +1,51 @@ |
|||
#!/bin/bash |
|||
|
|||
# load network |
|||
network=`cat .network` |
|||
|
|||
# get chain |
|||
chain="test" |
|||
isMainChain=$(sudo cat /mnt/hdd/${network}/${network}.conf 2>/dev/null | grep "#testnet=1" -c) |
|||
if [ ${isMainChain} -gt 0 ];then |
|||
chain="main" |
|||
fi |
|||
|
|||
# make sure qrcode-encoder in installed |
|||
clear |
|||
echo "*** Setup ***" |
|||
sudo apt-get install qrencode -y |
|||
|
|||
# get local IP |
|||
myip=$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p') |
|||
|
|||
clear |
|||
echo "******************************" |
|||
echo "Connect Shango Mobile Wallet" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "GETTING THE APP" |
|||
echo "At the moment this app is in public beta testing:" |
|||
echo "iOS: Read https://testflight.apple.com/join/WwCjFnS8 (open on device)" |
|||
echo "Android: https://play.google.com/apps/testing/com.shango (open on device)" |
|||
echo "" |
|||
echo "*** STEP 1 ***" |
|||
echo "Once you have the app is running make sure you are on the same local network (WLAN same as LAN)." |
|||
echo "Then go to --> 'Connect to your LND Server'" |
|||
echo "There you see three 3 form fields to fill out. Skip those and go right to the buttons below." |
|||
echo "" |
|||
echo "Click on the 'Scan OR' button" |
|||
echo "Make the this terminal as big as possible - fullscreen would be best." |
|||
echo "Then PRESS ENTER here in the terminal to generare the QR code and scan it with the app." |
|||
read key |
|||
|
|||
clear |
|||
echo "*** STEP 2 : SCAN MACAROON (make whole QR code fill camera) ***" |
|||
echo -e "${myip}:10009,\n$(xxd -p -c2000 ./.lnd/data/chain/bitcoin/mainnet/admin.macaroon)," > qr.txt && cat ./.lnd/tls.cert >>qr.txt && qrencode -t ANSIUTF8 < qr.txt |
|||
echo "(To shrink QR code: OSX->CMD- / LINUX-> CTRL-) Press ENTER when finished." |
|||
read key |
|||
|
|||
clear |
|||
echo "Now press 'Connect' within the Shango Wallet." |
|||
echo "If its not working - check issues on GitHub:" |
|||
echo "https://github.com/neogeno/shango-lightning-wallet/issues" |
|||
echo "" |
@ -0,0 +1,58 @@ |
|||
#!/bin/bash |
|||
|
|||
# load network |
|||
network=`cat .network` |
|||
|
|||
# get chain |
|||
chain="test" |
|||
isMainChain=$(sudo cat /mnt/hdd/${network}/${network}.conf 2>/dev/null | grep "#testnet=1" -c) |
|||
if [ ${isMainChain} -gt 0 ];then |
|||
chain="main" |
|||
fi |
|||
|
|||
# make sure qrcode-encoder in installed |
|||
clear |
|||
echo "*** Setup ***" |
|||
echo "" |
|||
echo "Installing zapconnect. Please wait..." |
|||
echo "" |
|||
echo "Getting github.com/LN-Zap/zapconnect ..." |
|||
go get -d github.com/LN-Zap/zapconnect |
|||
echo "" |
|||
echo "Building github.com/LN-Zap/zapconnect ..." |
|||
cd /home/admin/go/src/github.com/LN-Zap/zapconnect/ |
|||
go build -o /home/admin/go/src/github.com/LN-Zap/zapconnect/zapconnect |
|||
|
|||
|
|||
clear |
|||
echo "******************************" |
|||
echo "Connect Zap Mobile Wallet" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "GETTING THE APP" |
|||
echo "At the moment this app is in closed beta testing and the source code has not been published yet." |
|||
echo "Go to http://www.zap-ios.jackmallers.com sign up with your email (confirmation can take time)" |
|||
echo "iOS: Read https://developer.apple.com/testflight/testers/" |
|||
echo "" |
|||
echo "*** STEP 1 ***" |
|||
echo "Once you have the app is running make sure you are on the same local network (WLAN same as LAN)." |
|||
echo "" |
|||
echo "Click on Connect remote node" |
|||
echo "Make the this terminal as big as possible - fullscreen would be best." |
|||
echo "Then PRESS ENTER here in the terminal to generare the QR code and scan it with the app." |
|||
read key |
|||
|
|||
clear |
|||
echo "*** STEP 2 : Click on Scan (make whole QR code fill camera) ***" |
|||
|
|||
# If you drop the -i parameter, zapconnect will use the external IP. |
|||
/home/admin/go/src/github.com/LN-Zap/zapconnect/zapconnect -i |
|||
|
|||
echo "(To shrink QR code: OSX->CMD- / LINUX-> CTRL-) Press ENTER when finished." |
|||
read key |
|||
|
|||
clear |
|||
echo "If its not working - check issues on GitHub:" |
|||
echo "https://github.com/LN-Zap/zap-iOS/issues" |
|||
echo "https://github.com/LN-Zap/zapconnect/issues" |
|||
echo "" |
@ -0,0 +1,163 @@ |
|||
#!/usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
import base64 |
|||
import os |
|||
import signal |
|||
import subprocess |
|||
import sys |
|||
from optparse import OptionParser |
|||
|
|||
try: # make sure that (unsupported) Python2 can fail gracefully |
|||
import configparser |
|||
except ImportError: |
|||
pass |
|||
|
|||
if sys.version_info < (3, 5, 0): |
|||
print("Python2 not supported! Please run with Python3.5+") |
|||
sys.exit(1) |
|||
|
|||
|
|||
def sigint_handler(signum, frame): |
|||
print('CTRL+C pressed - exiting!') |
|||
sys.exit(0) |
|||
|
|||
|
|||
def _read_pwd(password_file): |
|||
# read and convert password from file |
|||
p = subprocess.run("sudo cat {}".format(password_file), |
|||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
|||
universal_newlines=False, shell=True, timeout=None) |
|||
if not p.returncode == 0: |
|||
print("unable to read password from: {}".format(password_file)) |
|||
sys.exit(1) |
|||
passwd_bytes = p.stdout.split(b"\n")[0] |
|||
passwd_b64 = base64.encodebytes(passwd_bytes).decode('utf-8').split("\n")[0] |
|||
return passwd_b64 |
|||
|
|||
|
|||
def _read_macaroon(lnd_macaroon_file): |
|||
# read and convert macaroon from file |
|||
p = subprocess.run("sudo xxd -ps -u -c 1000 {}".format(lnd_macaroon_file), |
|||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
|||
universal_newlines=True, shell=True, timeout=None) |
|||
macaroon_hex_dump = p.stdout.split("\n")[0] |
|||
return macaroon_hex_dump |
|||
|
|||
|
|||
def check_locked(password_file, lnd_cert_file, lnd_macaroon_file, host="localhost", port="8080", verbose=False): |
|||
# check locked |
|||
if verbose: |
|||
print("Checking for lock") |
|||
|
|||
passwd_b64 = _read_pwd(password_file) |
|||
macaroon_hex_dump = _read_macaroon(lnd_macaroon_file) |
|||
|
|||
cmds = ["curl", "-s", |
|||
"-H", "'Grpc-Metadata-macaroon: {}'".format(macaroon_hex_dump), |
|||
"--cacert", "{}".format(lnd_cert_file), |
|||
"-d", "{{\"wallet_password\": \"{}\"}}".format(passwd_b64), |
|||
"https://{}:{}/v1/getinfo".format(host, port)] |
|||
|
|||
p = subprocess.run(cmds, |
|||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
|||
universal_newlines=True, shell=False, timeout=None) |
|||
if not p.returncode == 0: |
|||
print("\033[91mSomething went wrong!\033[00m \033[93mIs lnd running? Wrong credentials?\033[00m") |
|||
# print("Returncode: {}".format(p.returncode)) |
|||
# print("Stderr: {}".format(p.stderr)) |
|||
sys.exit(1) |
|||
|
|||
if p.stdout == "Not Found\n": |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
|
|||
def unlock(password_file, lnd_cert_file, lnd_macaroon_file, host="localhost", port="8080", verbose=False): |
|||
if verbose: |
|||
print("Trying to unlock") |
|||
|
|||
passwd_b64 = _read_pwd(password_file) |
|||
macaroon_hex_dump = _read_macaroon(lnd_macaroon_file) |
|||
|
|||
# unlock lnd by calling curl |
|||
cmds = ["curl", "-s", |
|||
"-H", "'Grpc-Metadata-macaroon: {}'".format(macaroon_hex_dump), |
|||
"--cacert", "{}".format(lnd_cert_file), |
|||
"-d", "{{\"wallet_password\": \"{}\"}}".format(passwd_b64), |
|||
"https://{}:{}/v1/unlockwallet".format(host, port)] |
|||
|
|||
p = subprocess.run(cmds, |
|||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
|||
universal_newlines=True, shell=False, timeout=None) |
|||
if p.returncode == 0: |
|||
return True |
|||
else: |
|||
if verbose: |
|||
print("\033[91mSomething went wrong!\033[00m \033[93mIs lnd running? Wrong credentials?\033[00m") |
|||
# print("Returncode: {}".format(p.returncode)) |
|||
# print("Stderr: {}".format(p.stderr)) |
|||
return False |
|||
|
|||
|
|||
def main(): |
|||
signal.signal(signal.SIGINT, sigint_handler) |
|||
|
|||
usage = "usage: %prog [Options]" |
|||
parser = OptionParser(usage=usage, version="%prog {}".format("0.1")) |
|||
|
|||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", |
|||
help="Print more output") |
|||
|
|||
parser.add_option("-H", dest="host", type="string", default="localhost", |
|||
help="Host (default: localhost)") |
|||
parser.add_option("-P", dest="port", type="string", default="8080", |
|||
help="Port (default: 8080)") |
|||
|
|||
parser.add_option("-p", dest="password_file", type="string", default="pwd", |
|||
help="File containing *cleartext* password (default: pwd)") |
|||
parser.add_option("-c", dest="cert", type="string", |
|||
help="TLS certificate file (e.g. ~/.lnd/tls.cert)"), |
|||
parser.add_option("-m", dest="macaroon", type="string", |
|||
help="Macaroon file (e.g. readonly.macaroon)") |
|||
options, args = parser.parse_args() |
|||
|
|||
password_file = os.path.abspath(options.password_file) |
|||
if not os.path.exists(password_file): |
|||
print("Password file does not exist - exiting: {}".format(password_file)) |
|||
sys.exit(1) |
|||
|
|||
if options.cert: |
|||
lnd_cert_file = options.cert |
|||
else: |
|||
lnd_cert_file = "/home/bitcoin/.lnd/tls.cert" |
|||
|
|||
if options.macaroon: |
|||
lnd_macaroon_file = options.macaroon |
|||
else: |
|||
lnd_macaroon_file = "/home/bitcoin/.lnd/data/chain/bitcoin/mainnet/readonly.macaroon" |
|||
|
|||
if options.verbose: |
|||
print("Password File: \033[93m{}\033[00m".format(password_file)) |
|||
print("TLS CERT File: \033[93m{}\033[00m".format(lnd_cert_file)) |
|||
print("Macaroon File: \033[93m{}\033[00m".format(lnd_macaroon_file)) |
|||
print("URL: \033[93mhttps://{}:{}\033[00m".format(options.host, options.port)) |
|||
|
|||
if check_locked(password_file, lnd_cert_file, lnd_macaroon_file, |
|||
host=options.host, port=options.port, verbose=options.verbose): |
|||
if options.verbose: |
|||
print("\033[93m{}\033[00m".format("Locked")) |
|||
else: |
|||
print("\033[92m{}\033[00m".format("Not Locked")) |
|||
sys.exit(1) |
|||
|
|||
if unlock(password_file, lnd_cert_file, lnd_macaroon_file, |
|||
host=options.host, port=options.port, verbose=options.verbose): |
|||
print("\033[92m{}\033[00m".format("Successfully unlocked.")) |
|||
else: |
|||
print("\033[91m{}\033[00m".format("Failed to unlock.")) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
main() |
@ -0,0 +1,78 @@ |
|||
#!/bin/bash |
|||
_temp="./download/dialog.$$" |
|||
_error="./.error.out" |
|||
|
|||
# load network and chain info |
|||
network=`cat .network` |
|||
chain=$(${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain') |
|||
|
|||
# get available amount in on-chain wallet |
|||
maxAmount=$(lncli --chain=${network} walletbalance | grep '"confirmed_balance"' | cut -d '"' -f4) |
|||
|
|||
# TODO: pre-check if channels are open or are still in closing |
|||
# and let user know not all funds are available yet (just info Dialoge) |
|||
|
|||
# TODO: pre-check user hast more than 0 sat in on-chain wallet to send |
|||
|
|||
# let user enter the amount |
|||
l1="Enter the amount of funds you want to send/remove:" |
|||
l2="You have max available: ${maxAmount} sat" |
|||
l3="If you enter nothing, all funds available will be send." |
|||
dialog --title "Remove Funds from RaspiBlitz" \ |
|||
--inputbox "$l1\n$l2\n$l3" 10 60 2>$_temp |
|||
amount=$(cat $_temp | xargs) |
|||
shred $_temp |
|||
if [ ${#amount} -eq 0 ]; then |
|||
amount=${maxAmount} |
|||
fi |
|||
|
|||
# TODO: check if amount is in valid range |
|||
|
|||
# let user enter the address |
|||
l1="Enter the on-chain address to send funds to:" |
|||
l2="You will send: ${amount} sat to that address" |
|||
dialog --title "Where to send funds?" \ |
|||
--inputbox "$l1\n$l2" 8 65 2>$_temp |
|||
address=$(cat $_temp | xargs) |
|||
shred $_temp |
|||
if [ ${#address} -eq 0 ]; then |
|||
echo "FAIL - not a valid address (${address})" |
|||
exit 1 |
|||
fi |
|||
|
|||
# TODO: check address is valid for network and chain |
|||
|
|||
# TODO: check if fees are getting done right so that transaction will get processed |
|||
amount=$((amount - 10000)) |
|||
command="lncli --chain=${network} sendcoins --addr ${address} --amt ${amount} --conf_target 3" |
|||
|
|||
|
|||
clear |
|||
echo "******************************" |
|||
echo "Send on-chain Funds" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "COMMAND LINE: " |
|||
echo $command |
|||
echo "" |
|||
echo "RESULT:" |
|||
|
|||
# execute command |
|||
if [ ${#command} -gt 0 ]; then |
|||
result=$($command) |
|||
fi |
|||
|
|||
# on no result |
|||
if [ ${#result} -eq 0 ]; then |
|||
echo "Sorry something went wrong - thats unusual." |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# when result is available |
|||
echo "$result" |
|||
|
|||
# TODO: check if all cashed out (0 funds + 0 channels) -> let user knwo its safe to update/reset RaspiBlitz |
|||
|
|||
echo "OK. That worked :)" |
|||
echo "" |
@ -0,0 +1,36 @@ |
|||
#!/bin/bash |
|||
|
|||
# load network and chain info |
|||
network=`cat .network` |
|||
chain=$(${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain') |
|||
|
|||
command="lncli closeallchannels --force" |
|||
|
|||
clear |
|||
echo "***********************************" |
|||
echo "Closing All Channels (EXPERIMENTAL)" |
|||
echo "***********************************" |
|||
echo "" |
|||
echo "COMMAND LINE: " |
|||
echo $command |
|||
echo "" |
|||
echo "RESULT:" |
|||
|
|||
# PRECHECK) check if chain is in sync |
|||
chainInSync=$(lncli --chain=${network} getinfo | grep '"synced_to_chain": true' -c) |
|||
if [ ${chainInSync} -eq 0 ]; then |
|||
command="" |
|||
result="FAIL PRECHECK - lncli getinfo shows 'synced_to_chain': false - wait until chain is sync " |
|||
fi |
|||
|
|||
# execute command |
|||
if [ ${#command} -gt 0 ]; then |
|||
${command} |
|||
fi |
|||
|
|||
echo "" |
|||
echo "OK - wait a 5 seconds" |
|||
sleep 5 |
|||
|
|||
echo "Your Open Channel List (to check):" |
|||
lnchannels |
@ -0,0 +1,92 @@ |
|||
#!/bin/bash |
|||
_temp="./download/dialog.$$" |
|||
_error="./.error.out" |
|||
sudo chmod 7777 ${_error} |
|||
|
|||
# load network and chain info |
|||
network=`cat .network` |
|||
chain=$(sudo -bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain') |
|||
|
|||
echo "" |
|||
echo "*** Precheck ***" |
|||
|
|||
# check if chain is in sync |
|||
chainInSync=$(lncli --chain=${network} getinfo | grep '"synced_to_chain": true' -c) |
|||
if [ ${chainInSync} -eq 0 ]; then |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL - 'lncli getinfo' shows 'synced_to_chain': false" |
|||
echo "Wait until chain is sync with LND and try again." |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# check number of connected peers |
|||
echo "check for open channels" |
|||
openChannels=$(sudo -u bitcoin /usr/local/bin/lncli --chain=${network} listchannels 2>/dev/null | grep chan_id -c) |
|||
if [ ${openChannels} -eq 0 ]; then |
|||
echo "" |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL - You have NO ESTABLISHED CHANNELS .. open a channel first." |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# let user enter the invoice |
|||
l1="Enter the AMOUNT IN SATOSHI of the invoice:" |
|||
l2="1 ${network} = 100 000 000 SAT" |
|||
dialog --title "Pay thru Lightning Network" \ |
|||
--inputbox "$l1\n$l2" 9 50 2>$_temp |
|||
amount=$(cat $_temp | xargs | tr -dc '0-9') |
|||
shred $_temp |
|||
if [ ${#amount} -eq 0 ]; then |
|||
echo "FAIL - not a valid input (${amount})" |
|||
exit 1 |
|||
fi |
|||
|
|||
# build command |
|||
command="lncli --chain=${network} addinvoice ${amount}" |
|||
|
|||
# info output |
|||
clear |
|||
echo "******************************" |
|||
echo "Create Invoice / Payment Request" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "COMMAND LINE: " |
|||
echo $command |
|||
echo "" |
|||
echo "RESULT:" |
|||
sleep 2 |
|||
|
|||
# execute command |
|||
result=$($command 2>$_error) |
|||
error=`cat ${_error}` |
|||
|
|||
#echo "result(${result})" |
|||
#echo "error(${error})" |
|||
|
|||
if [ ${#error} -gt 0 ]; then |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL" |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "${error}" |
|||
else |
|||
echo "******************************" |
|||
echo "WIN" |
|||
echo "******************************" |
|||
echo "${result}" |
|||
echo "" |
|||
rhash=$(echo "$result" | grep r_hash | cut -d '"' -f4) |
|||
payReq=$(echo "$result" | grep pay_req | cut -d '"' -f4) |
|||
echo "Give this Invoice/PaymentRequest to someone to pay it:" |
|||
echo "" |
|||
echo ${payReq} |
|||
echo "" |
|||
echo "You can use 'lncli --chain=${network} lookupinvoice ${rhash}' to check the payment. " |
|||
|
|||
# TODO: Offer to go into monitor for incommin payment loop. |
|||
|
|||
fi |
|||
echo "" |
@ -0,0 +1,136 @@ |
|||
#!/bin/bash |
|||
_temp="./download/dialog.$$" |
|||
_error="./.error.out" |
|||
|
|||
# load network and chain info |
|||
network=`cat .network` |
|||
chain=$(sudo -bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain') |
|||
|
|||
echo "" |
|||
echo "*** Precheck ***" |
|||
|
|||
# check if chain is in sync |
|||
chainInSync=$(lncli --chain=${network} getinfo | grep '"synced_to_chain": true' -c) |
|||
if [ ${chainInSync} -eq 0 ]; then |
|||
echo "FAIL - 'lncli getinfo' shows 'synced_to_chain': false" |
|||
echo "Wait until chain is sync with LND and try again." |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# check available funding |
|||
confirmedBalance=$(lncli --chain=${network} walletbalance | grep '"confirmed_balance"' | cut -d '"' -f4) |
|||
if [ ${confirmedBalance} -eq 0 ]; then |
|||
echo "FAIL - You have 0 SATOSHI in your confirmed LND On-Chain Wallet." |
|||
echo "Please fund your on-chain wallet first and wait until confirmed." |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# check number of connected peers |
|||
numConnectedPeers=$(lncli --chain=${network} listpeers | grep pub_key -c) |
|||
if [ ${numConnectedPeers} -eq 0 ]; then |
|||
echo "FAIL - no peers connected on lightning network" |
|||
echo "You can only open channels to peer nodes to connected to first." |
|||
echo "Use CONNECT peer option in main menu first." |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# let user pick a peer to open a channels with |
|||
OPTIONS=() |
|||
while IFS= read -r grepLine |
|||
do |
|||
pubKey=$(echo ${grepLine} | cut -d '"' -f4) |
|||
#echo "grepLine(${pubKey})" |
|||
OPTIONS+=(${pubKey} "") |
|||
done < <(lncli --chain=${network} listpeers | grep pub_key) |
|||
TITLE="Open (Payment) Channel" |
|||
MENU="\nChoose a peer you connected to, to open the channel with: \n " |
|||
pubKey=$(dialog --clear \ |
|||
--title "$TITLE" \ |
|||
--menu "$MENU" \ |
|||
14 73 5 \ |
|||
"${OPTIONS[@]}" \ |
|||
2>&1 >/dev/tty) |
|||
|
|||
clear |
|||
if [ ${#pubKey} -eq 0 ]; then |
|||
echo "Selected CANCEL" |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
# find out what is the minimum amount |
|||
# TODO find a better way - also consider dust and channel reserve |
|||
# details see here: https://github.com/btcontract/lnwallet/issues/52 |
|||
minSat=20000 |
|||
if [ "${network}" = "bitcoin" ]; then |
|||
minSat=250000 |
|||
fi |
|||
_error="./.error.out" |
|||
lncli --chain=${network} openchannel ${CHOICE} 1 0 2>$_error |
|||
error=`cat ${_error}` |
|||
if [ $(echo "${error}" | grep "channel is too small" -c) -eq 1 ]; then |
|||
minSat=$(echo "${error}" | tr -dc '0-9') |
|||
fi |
|||
|
|||
# let user enter a amount |
|||
l1="Amount in SATOSHI to fund this channel:" |
|||
l2="min required : ${minSat}" |
|||
l3="max available : ${confirmedBalance}" |
|||
dialog --title "Funding of Channel" \ |
|||
--inputbox "$l1\n$l2\n$l3" 10 60 2>$_temp |
|||
amount=$(cat $_temp | xargs | tr -dc '0-9') |
|||
shred $_temp |
|||
if [ ${#amount} -eq 0 ]; then |
|||
echo "FAIL - not a valid input (${amount})" |
|||
exit 1 |
|||
fi |
|||
|
|||
# build command |
|||
command="lncli --chain=${network} openchannel ${pubKey} ${amount} 0" |
|||
|
|||
# info output |
|||
clear |
|||
echo "******************************" |
|||
echo "Open Channel" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "COMMAND LINE: " |
|||
echo $command |
|||
echo "" |
|||
echo "RESULT:" |
|||
|
|||
# execute command |
|||
result=$($command 2>$_error) |
|||
error=`cat ${_error}` |
|||
|
|||
#echo "result(${result})" |
|||
#echo "error(${error})" |
|||
|
|||
if [ ${#error} -gt 0 ]; then |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL" |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "${error}" |
|||
else |
|||
echo "******************************" |
|||
echo "WIN" |
|||
echo "******************************" |
|||
echo "${result}" |
|||
echo "" |
|||
echo "Whats next? --> You need to wait 6 confirmations, for the channel to be ready." |
|||
fundingTX=$(echo "${result}" | grep 'funding_txid' | cut -d '"' -f4) |
|||
if [ "${network}" = "bitcoin" ]; then |
|||
if [ "${chain}" = "main" ]; then |
|||
echo "https://blockexplorer.com/tx/${fundingTX}" |
|||
else |
|||
echo "https://testnet.blockexplorer.com/tx/${fundingTX}" |
|||
fi |
|||
fi |
|||
if [ "${network}" = "litecoin" ]; then |
|||
echo "https://live.blockcypher.com/ltc/tx/${fundingTX}/" |
|||
fi |
|||
fi |
|||
echo "" |
@ -0,0 +1,106 @@ |
|||
#!/bin/bash |
|||
_temp="./download/dialog.$$" |
|||
_error="./.error.out" |
|||
|
|||
# load network and chain info |
|||
network=`cat .network` |
|||
chain=$(sudo -bitcoin ${network}-cli -datadir=/home/bitcoin/.${network} getblockchaininfo | jq -r '.chain') |
|||
|
|||
echo "" |
|||
echo "*** Precheck ***" |
|||
|
|||
# check if chain is in sync |
|||
chainInSync=$(lncli --chain=${network} getinfo | grep '"synced_to_chain": true' -c) |
|||
if [ ${chainInSync} -eq 0 ]; then |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL - 'lncli getinfo' shows 'synced_to_chain': false" |
|||
echo "Wait until chain is sync with LND and try again." |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
|
|||
# check number of connected peers |
|||
echo "check for open channels" |
|||
openChannels=$(sudo -u bitcoin /usr/local/bin/lncli --chain=${network} listchannels 2>/dev/null | grep chan_id -c) |
|||
if [ ${openChannels} -eq 0 ]; then |
|||
echo "" |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL - You have NO ESTABLISHED CHANNELS .. open a channel first." |
|||
echo "!!!!!!!!!!!!!!!!!!!" |
|||
echo "" |
|||
exit 1 |
|||
fi |
|||
|
|||
paymentRequestStart="???" |
|||
if [ "${network}" = "bitcoin" ]; then |
|||
if [ "${chain}" = "main" ]; then |
|||
paymentRequestStart="lnbc" |
|||
else |
|||
paymentRequestStart="lntb" |
|||
fi |
|||
elif [ "${network}" = "litecoin" ]; then |
|||
paymentRequestStart="lnltc" |
|||
fi |
|||
|
|||
testSite="???" |
|||
if [ "${network}" = "bitcoin" ]; then |
|||
if [ "${chain}" = "main" ]; then |
|||
testSite="https://satoshis.place" |
|||
else |
|||
testSite="https://testnet.satoshis.place" |
|||
fi |
|||
elif [ "${network}" = "litecoin" ]; then |
|||
testSite="https://millionlitecoinhomepage.net" |
|||
fi |
|||
|
|||
# let user enter the invoice |
|||
l1="Copy the LightningInvoice/PaymentRequest into here:" |
|||
l2="Its a long string starting with '${paymentRequestStart}'" |
|||
l3="To try it out go to: ${testSite}" |
|||
dialog --title "Pay thru Lightning Network" \ |
|||
--inputbox "$l1\n$l2\n$l3" 10 70 2>$_temp |
|||
invoice=$(cat $_temp | xargs) |
|||
shred $_temp |
|||
if [ ${#invoice} -eq 0 ]; then |
|||
echo "FAIL - not a valid input (${invoice})" |
|||
exit 1 |
|||
fi |
|||
|
|||
# TODO: maybe try/show the decoded info first by using https://api.lightning.community/#decodepayreq |
|||
|
|||
# build command |
|||
command="lncli --chain=${network} sendpayment --force --pay_req=${invoice}" |
|||
|
|||
# info output |
|||
clear |
|||
echo "******************************" |
|||
echo "Pay Invoice / Payment Request" |
|||
echo "******************************" |
|||
echo "" |
|||
echo "COMMAND LINE: " |
|||
echo $command |
|||
echo "" |
|||
echo "RESULT (may wait in case of timeout):" |
|||
|
|||
# execute command |
|||
result=$($command 2>$_error) |
|||
error=`cat ${_error}` |
|||
|
|||
#echo "result(${result})" |
|||
#echo "error(${error})" |
|||
|
|||
if [ ${#error} -gt 0 ]; then |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "FAIL" |
|||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" |
|||
echo "${error}" |
|||
else |
|||
echo "${result}" |
|||
echo "******************************" |
|||
echo "WIN" |
|||
echo "******************************" |
|||
echo "It worked :) - check out the service you were paying." |
|||
fi |
|||
echo "" |
@ -0,0 +1,17 @@ |
|||
#!/bin/bash |
|||
echo "" |
|||
echo "*** UPDATING SHELL SCRIPTS FROM GITHUB ***" |
|||
echo "justincase, not the final upadte mechanism" |
|||
echo "******************************************" |
|||
cd /home/admin/raspiblitz |
|||
git pull |
|||
cd .. |
|||
rm *.sh |
|||
rm -r assets |
|||
sudo -u admin cp /home/admin/raspiblitz/home.admin/*.* /home/admin |
|||
sudo -u admin chmod +x *.sh |
|||
sudo -u admin cp -r /home/admin/raspiblitz/home.admin/assets /home/admin/ |
|||
echo "******************************************" |
|||
echo "OK - shell scripts and assests are up to date" |
|||
echo "Reboot recommended" |
|||
echo "" |
@ -0,0 +1,140 @@ |
|||
#!/bin/bash |
|||
|
|||
# This script runs on every start calles by boostrap.service |
|||
# It makes sure that the system is configured like the |
|||
# default values or as in the config. |
|||
# For more details see background_raspiblitzSettings.md |
|||
|
|||
# load codeVersion |
|||
source /home/admin/_version.info |
|||
|
|||
logfile="/home/admin/raspiblitz.log" |
|||
echo "Writing logs to: ${logfile}" |
|||
echo "" > $logfile |
|||
echo "***********************************************" >> $logfile |
|||
echo "Running RaspiBlitz Bootstrap ${codeVersion}" >> $logfile |
|||
date >> $logfile |
|||
echo "***********************************************" >> $logfile |
|||
|
|||
|
|||
################################ |
|||
# AFTER BOOT SCRIPT |
|||
# when a process needs to |
|||
# execute stuff after a reboot |
|||
# /home/admin/setup.sh |
|||
################################ |
|||
|
|||
# check for after boot script |
|||
afterSetupScriptExists=$(ls /home/admin/setup.sh 2>/dev/null | grep -c setup.sh) |
|||
if [ ${afterSetupScriptExists} -eq 1 ]; then |
|||
echo "*** SETUP SCRIPT DETECTED ***" |
|||
# echo out script to journal logs |
|||
sudo cat /home/admin/setup.sh |
|||
# execute the after boot script |
|||
sudo /home/admin/setup.sh |
|||
# delete the after boot script |
|||
sudo rm /home/admin/setup.sh |
|||
# reboot again |
|||
echo "DONE wait 6 secs ... one more reboot needed ... " |
|||
sudo shutdown -r now |
|||
sleep 100 |
|||
fi |
|||
|
|||
|
|||
################################ |
|||
# PUBLIC IP |
|||
# for LND on startup |
|||
################################ |
|||
printf "PUBLICIP=$(curl -vv ipinfo.io/ip 2> /run/publicip.log)\n" > /run/publicip; |
|||
chmod 774 /run/publicip |
|||
|
|||
|
|||
################################ |
|||
# HDD CHECK / INIT |
|||
# for the very first setup |
|||
################################ |
|||
|
|||
# check if the HDD is mounted |
|||
hddAvailable=$(ls -la /mnt/hdd 2>/dev/null) |
|||
if [ ${#hddAvailable} -eq 0 ]; then |
|||
echo "HDD is NOT available" >> $logfile |
|||
echo "TODO: Try to mount." |
|||
exit 1 |
|||
fi |
|||
|
|||
################################ |
|||
# CONFIGFILE BASICS |
|||
################################ |
|||
|
|||
# check if there is a config file |
|||
configFile="/mnt/hdd/raspiblitz.conf" |
|||
configExists=$(ls ${configFile} 2>/dev/null | grep -c '.conf') |
|||
if [ ${configExists} -eq 0 ]; then |
|||
|
|||
# create new config |
|||
echo "creating config file: ${configFile}" >> $logfile |
|||
echo "# RASPIBLITZ CONFIG FILE" > $configFile |
|||
echo "raspiBlitzVersion='${version}'" >> $configFile |
|||
sudo chmod 777 ${configFile} |
|||
|
|||
else |
|||
|
|||
# load & check config version |
|||
source $configFile |
|||
if [ "${raspiBlitzVersion}" != "${raspiBlitzVersion}" ]; then |
|||
echo "detected version change ... starting migration script" >> $logfile |
|||
/home/admin/_migrateVersion.sh |
|||
fi |
|||
|
|||
fi |
|||
|
|||
################################ |
|||
# DEFAULT VALUES |
|||
################################ |
|||
|
|||
# AUTOPILOT |
|||
# autoPilot=off|on |
|||
if [ ${#autoPilot} -eq 0 ]; then |
|||
echo "autoPilot=off" >> $configFile |
|||
fi |
|||
|
|||
# after all default values written to config - reload config |
|||
source $configFile |
|||
|
|||
|
|||
################################ |
|||
# AUTOPILOT |
|||
################################ |
|||
|
|||
echo "" >> $logfile |
|||
echo "** AUTOPILOT" >> $logfile |
|||
|
|||
# check if LND is installed |
|||
lndExists=$(ls /mnt/hdd/lnd/lnd.conf 2>/dev/null | grep -c '.conf') |
|||
if [ ${lndExists} -eq 1 ]; then |
|||
|
|||
# check if autopilot is active in LND config |
|||
lndAutopilot=$( grep -c "autopilot.active=1" /mnt/hdd/lnd/lnd.conf ) |
|||
echo "confAutopilot(${autoPilot})" >> $logfile |
|||
echo "lndAutopilot(${lndAutopilot})" >> $logfile |
|||
|
|||
# switch on |
|||
if [ ${lndAutopilot} -eq 0 ] && [ "${autoPilot}" = "on" ]; then |
|||
echo "switching the LND autopilot ON" >> $logfile |
|||
sudo sed -i "s/^autopilot.active=.*/autopilot.active=1/g" /mnt/hdd/lnd/lnd.conf |
|||
fi |
|||
|
|||
# switch off |
|||
if [ ${lndAutopilot} -eq 1 ] && [ "${autoPilot}" = "off" ]; then |
|||
echo "switching the LND autopilot OFF" >> $logfile |
|||
sudo sed -i "s/^autopilot.active=.*/autopilot.active=0/g" /mnt/hdd/lnd/lnd.conf |
|||
fi |
|||
|
|||
else |
|||
|
|||
echo "WARNING: /mnt/hdd/lnd/lnd.conf does not exists. Setup needs to run properly first!" >> $logfile |
|||
|
|||
fi |
|||
|
|||
echo "" >> $logfile |
|||
echo "DONE BOOTSTRAP" >> $logfile |
@ -0,0 +1,17 @@ |
|||
#!/bin/bash |
|||
|
|||
# load codeVersion |
|||
source ./_version.info |
|||
|
|||
# load raspiblitz config |
|||
source /mnt/hdd/raspiblitz.conf |
|||
|
|||
echo "" |
|||
echo "*****************************" |
|||
echo "Version Migration RaspiBlitz" |
|||
echo "*****************************" |
|||
echo "Version Code: ${codeVersion}" |
|||
echo "Version Data: ${raspiBlitzVersion}" |
|||
|
|||
echo "TODO: Update Migration check ..." |
|||
echo "" |
@ -0,0 +1,2 @@ |
|||
# RaspiBlitz Version - always [main].[sub] |
|||
codeVersion="0.95" |
@ -0,0 +1,16 @@ |
|||
# Boostrap the RaspiBlitz |
|||
# /etc/systemd/system/bootstrap.service |
|||
|
|||
[Unit] |
|||
Description=setting up RaspiBlitz and enforcing the config on every startup |
|||
After=network.target |
|||
|
|||
[Service] |
|||
User=root |
|||
Group=root |
|||
Type=oneshot |
|||
ExecStart=/home/admin/_bootstrap.sh |
|||
StandardOutput=journal |
|||
|
|||
[Install] |
|||
WantedBy=multi-user.target |
@ -1,22 +1,32 @@ |
|||
# lnd configuration |
|||
|
|||
[Application Options] |
|||
debuglevel=info |
|||
debughtlc=true |
|||
debuglevel=debug |
|||
maxpendingchannels=5 |
|||
# LINE7 -insert-> alias=[ALIAS] |
|||
alias=raspiblitz |
|||
color=#68F442 |
|||
nat=false |
|||
|
|||
# RPC open to all connections on Port 10009 |
|||
rpclisten=0.0.0.0:10009 |
|||
# Domain, could use https://freedns.afraid.org |
|||
#tlsextradomain=lightning.yourhost.com |
|||
|
|||
[Bitcoin] |
|||
bitcoin.active=1 |
|||
|
|||
bitcoin.node=bitcoind |
|||
# enable either testnet or mainnet |
|||
bitcoin.testnet=1 |
|||
#bitcoin.mainnet=1 |
|||
#bitcoin.testnet=1 |
|||
bitcoin.mainnet=1 |
|||
|
|||
bitcoin.node=bitcoind |
|||
[Bitcoind] |
|||
bitcoind.rpcuser=raspibolt |
|||
bitcoind.rpcpass=passwordB |
|||
bitcoind.rpchost=127.0.0.1 |
|||
bitcoind.zmqpubrawblock=tcp://*:28332 |
|||
bitcoind.zmqpubrawtx=tcp://*:28333 |
|||
|
|||
[autopilot] |
|||
autopilot.active=1 |
|||
autopilot.active=0 |
|||
autopilot.maxchannels=5 |
|||
autopilot.allocation=0.6 |
|||
|
@ -1,21 +1,30 @@ |
|||
# lnd configuration |
|||
|
|||
[Application Options] |
|||
debuglevel=info |
|||
debughtlc=true |
|||
debuglevel=debug |
|||
maxpendingchannels=5 |
|||
# LINE7 -insert-> alias=[ALIAS] |
|||
alias=raspiblitz |
|||
color=#68F442 |
|||
nat=false |
|||
|
|||
# RPC open to all connections on Port 10009 |
|||
rpclisten=0.0.0.0:10009 |
|||
# Domain, could use https://freedns.afraid.org |
|||
#tlsextradomain=lightning.yourhost.com |
|||
|
|||
[Litecoin] |
|||
litecoin.active=1 |
|||
litecoin.mainnet=1 |
|||
litecoin.node=litecoind |
|||
litecoind.rpcuser=jason |
|||
litecoind.rpcpass=litecoin |
|||
litecoind.zmqpath=tcp://127.0.0.1:28332 |
|||
|
|||
[Litecoind] |
|||
litecoind.rpchost=127.0.0.1 |
|||
litecoind.rpcuser=raspibolt |
|||
litecoind.rpcpass=passwordB |
|||
litecoind.zmqpubrawblock=tcp://*:28332 |
|||
litecoind.zmqpubrawtx=tcp://*:28333 |
|||
|
|||
[autopilot] |
|||
autopilot.active=1 |
|||
autopilot.active=0 |
|||
autopilot.maxchannels=5 |
|||
autopilot.allocation=0.6 |
|||
|
@ -0,0 +1,32 @@ |
|||
# RaspiBlitz: systemd unit for lnd |
|||
|
|||
[Unit] |
|||
Description=LND Lightning Daemon |
|||
Wants=bitcoind.service |
|||
After=bitcoind.service |
|||
|
|||
# for use with sendmail alert |
|||
#OnFailure=systemd-sendmail@%n |
|||
|
|||
[Service] |
|||
# get var PUBIP from file |
|||
EnvironmentFile=/mnt/hdd/tor/v3Address |
|||
|
|||
# TOR Hidden Service v2 |
|||
ExecStart=/usr/local/bin/lnd --tor.active --tor.v2 --listen=127.0.0.1:9735 |
|||
|
|||
# TOR Hidden Service v3 |
|||
# ExecStart=/usr/local/bin/lnd --tor.active --tor.v3 --externalip=${V3ADDRESS} --listen=127.0.0.1:9735 |
|||
|
|||
PIDFile=/home/bitcoin/.lnd/lnd.pid |
|||
User=bitcoin |
|||
Group=bitcoin |
|||
LimitNOFILE=128000 |
|||
Type=simple |
|||
KillMode=process |
|||
TimeoutSec=180 |
|||
Restart=always |
|||
RestartSec=60 |
|||
|
|||
[Install] |
|||
WantedBy=multi-user.target |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 244 KiB |
After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 40 KiB |
@ -1,70 +0,0 @@ |
|||
######################################################################### |
|||
# Build your SD card image based on: |
|||
# RASPBIAN STRETCH WITH DESKTOP (2018-06-27) |
|||
# https://www.raspberrypi.org/downloads/raspbian/ |
|||
# SHA256: 8636ab9fdd8f58a8ec7dde33b83747696d31711d17ef68267dbbcd6cfb968c24 |
|||
########################################################################## |
|||
# setup fresh SD card with image above - login per SSH and run this script |
|||
########################################################################## |
|||
|
|||
# *** RASPI CONFIG *** |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#raspi-config |
|||
|
|||
# A) Set Raspi to boot up automatically with user pi (for the LCD) |
|||
# https://www.raspberrypi.org/forums/viewtopic.php?t=21632 |
|||
sudo raspi-config nonint do_boot_behaviour B2 |
|||
|
|||
# B) Give Raspi a default hostname (optional) |
|||
sudo raspi-config nonint do_hostname "RaspiBlitz" |
|||
|
|||
# do memory split (16MB) |
|||
# TODO: sudo raspi-config nonint do_memory_split %d |
|||
|
|||
# *** SOFTWARE UPDATE *** |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#software-update |
|||
|
|||
sudo apt-get update |
|||
sudo apt-get upgrade |
|||
sudo apt-get install htop git curl bash-completion jq dphys-swapfile |
|||
|
|||
# *** ADDING MAIN USER "admin" *** |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-main-user-admin |
|||
# using the default password 'raspiblitz' |
|||
|
|||
# TODO: set password automatically |
|||
sudo adduser admin |
|||
sudo adduser admin sudo |
|||
sudo chsh admin -s /bin/bash |
|||
sudo passwd root |
|||
|
|||
# TODO |
|||
# $ sudo visudo |
|||
# %sudo ALL=(ALL:ALL) ALL |
|||
# %sudo ALL=(ALL) NOPASSWD:ALL |
|||
|
|||
# *** ADDING SERVICE USER “bitcoin” |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-the-service-user-bitcoin |
|||
|
|||
sudo adduser bitcoin |
|||
|
|||
# *** SWAP FILE *** |
|||
# based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#moving-the-swap-file |
|||
# but just deactivating and deleting old (will be created alter when user adds HDD) |
|||
|
|||
sudo dphys-swapfile swapoff |
|||
sudo dphys-swapfile uninstall |
|||
|
|||
# --> CONTINUE: https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#hardening-your-pi |
|||
|
|||
# *** TODOS / DECIDE / GIVE MANUAL INTRUCTIONS ****** |
|||
|
|||
# ??? |
|||
# sudo raspi-config nonint do_ssh %d |
|||
|
|||
# Wait for network at boot? |
|||
# sudo raspi-config nonint get_boot_wait |
|||
# sudo raspi-config nonint do_boot_wait %d |
|||
|
|||
# automaticall detect and set time zone? |
|||
# maybe do on in setup scripts |
|||
|
@ -0,0 +1,21 @@ |
|||
## Hardware Needed (Taobao.com/T-Mall Shopping List) |
|||
|
|||
*The RaspiBlitz software is build and tested for the following Hardware set that you can buy cheap on taobao.com:* |
|||
|
|||
Set (¥378.00 CNY) |
|||
* RaspBerry Pi 3 B+ |
|||
* Micro SD-Card 16GB |
|||
* Power Unit 2.5A |
|||
* several items (e.g. cooling fins) |
|||
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.46982e8dibAzMc&id=550270480898&_u=b110k44d3302 |
|||
|
|||
Single HDD (Bulk) |
|||
* 1TB Hard Drive (¥379.00 CNY) |
|||
https://detail.tmall.com/item.htm?id=38476523976&spm=a1z09.2.0.0.46982e8dibAzMc&_u=b110k44d53f4&sku_properties=5919063:6536025 |
|||
|
|||
Set (¥119.00 CNY) |
|||
* LCD-Display |
|||
* Case (acrylic) |
|||
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.46982e8dibAzMc&id=576167736126&_u=b110k44df816 |
|||
|
|||
**Total Price: ¥876.00 CNY** approx. 111 EUR or 128 USD |
@ -1,3 +1,8 @@ |
|||
Your are from the UK? Your help is needed! |
|||
*The RaspiBlitz software is build and tested for the following Hardware set that you can buy cheap on Amazon.co.uk:* |
|||
|
|||
It would be great if you can create a shopping list for amazon UK for the RaspiBlitz and test if everything is running with the hardware setup. Thx. |
|||
* RaspBerry Pi 3 https://www.amazon.co.uk/Raspberry-Pi-Model-64-Bit-Processor/dp/B07BDR5PDW |
|||
* Micro SD-Card 16GB https://www.amazon.co.uk/Kingston-SDC10G2-16GB-microSDHC-Included/dp/B0162YQEIE |
|||
* Power https://www.amazon.co.uk/iTrunk-Raspberry-Model-Supply-Charger/dp/B01MFFSPHE |
|||
* 1TB Hard Drive https://www.amazon.co.uk/Toshiba-Canvio-Basics-Portable-External/dp/B00KWHJY7Q |
|||
* Case https://www.amazon.co.uk/SB-Components-Transparent-Case-Raspberry/dp/B0173GQF8Y |
|||
* LCD-Display https://www.amazon.co.uk/ELEGOO-480x320-Raspberry-Monitor-Interface/dp/B01MRQTMTD |
Loading…
Reference in new issue