From ae6f3794081553e02afc555d1370efe5509376e5 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Wed, 13 Mar 2019 16:04:09 +0100 Subject: [PATCH 01/21] #418 cleaning system logs on startup --- home.admin/_bootstrap.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index 814219c..4d4c160 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -58,6 +58,11 @@ if [ "${setupStep}" != "100" ]; then fi sudo chmod 777 ${infoFile} +# cleaning system logs to prevent SD card filling up +# see https://github.com/rootzoll/raspiblitz/issues/418#issuecomment-472180944 +echo "Cleaning system logs" +sudo rm /var/log/daemon* && sudo rm /var/log/*.gz + ################################ # GENERATE UNIQUE SSH PUB KEYS # on first boot up From e2d14aa4aa1fa5c15ca6f76b3370b96509082cfa Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Wed, 13 Mar 2019 16:09:01 +0100 Subject: [PATCH 02/21] #394 LND debuglogs to info --- home.admin/assets/lnd.bitcoin.conf | 2 +- home.admin/assets/lnd.litecoin.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/home.admin/assets/lnd.bitcoin.conf b/home.admin/assets/lnd.bitcoin.conf index 05537ec..a6db19e 100755 --- a/home.admin/assets/lnd.bitcoin.conf +++ b/home.admin/assets/lnd.bitcoin.conf @@ -1,7 +1,7 @@ # lnd configuration [Application Options] -debuglevel=debug +debuglevel=info maxpendingchannels=5 alias=raspiblitz color=#68F442 diff --git a/home.admin/assets/lnd.litecoin.conf b/home.admin/assets/lnd.litecoin.conf index bf9bdc1..4f50520 100755 --- a/home.admin/assets/lnd.litecoin.conf +++ b/home.admin/assets/lnd.litecoin.conf @@ -1,7 +1,7 @@ # lnd configuration [Application Options] -debuglevel=debug +debuglevel=info maxpendingchannels=5 alias=raspiblitz color=#68F442 From 97b8feaa1a91e5ad62422e93cf8d0bcef583e4ba Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Wed, 13 Mar 2019 17:00:44 +0100 Subject: [PATCH 03/21] #394 change logrotate config --- build_sdcard.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/build_sdcard.sh b/build_sdcard.sh index ede9eeb..d39edf2 100644 --- a/build_sdcard.sh +++ b/build_sdcard.sh @@ -71,6 +71,11 @@ else echo "OK running ${baseImage}" fi +# setting static DNS server +# see https://github.com/rootzoll/raspiblitz/issues/322#issuecomment-466733550 +sudo sed -i "s/^#static domain_name_servers=192.168.0.1*/static domain_name_servers=1.1.1.1/g" /etc/dhcpcd.conf +systemctl daemon-reload + # fixing locales for build # https://github.com/rootzoll/raspiblitz/issues/138 # https://daker.me/2014/10/how-to-fix-perl-warning-setting-locale-failed-in-raspbian.html @@ -148,6 +153,27 @@ sudo bash -c "echo '[Service]' >> /etc/systemd/system/getty@tty1.service.d/autol 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" +# change log rotates +# see https://github.com/rootzoll/raspiblitz/issues/394#issuecomment-471535483 +sudo head -n 18 /etc/logrotate.d/rsyslog > ./rsyslog +echo "{" >> ./rsyslog +echo " rotate 4" >> ./rsyslog +echo " size=100M" >> ./rsyslog +echo " missingok" >> ./rsyslog +echo " notifempty" >> ./rsyslog +echo " compress" >> ./rsyslog +echo " delaycompress" >> ./rsyslog +echo " sharedscripts" >> ./rsyslog +echo " postrotate" >> ./rsyslog +echo " invoke-rc.d rsyslog rotate > /dev/null" >> ./rsyslog +echo " endscript" >> ./rsyslog +echo "}" >> ./rsyslog +echo "" >> ./rsyslog +sudo tail -n +19 /etc/logrotate.d/rsyslog >> ./rsyslog +sudo mv ./rsyslog /etc/logrotate.d/rsyslog +sudo chown root:root /etc/logrotate.d/rsyslog +sudo service rsyslog restart + echo "" echo "*** SOFTWARE UPDATE ***" # based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#software-update From 0b92392db96fa97d5d504fd775200b430dcd44b8 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Wed, 13 Mar 2019 17:38:08 +0100 Subject: [PATCH 04/21] version number to 1.1 --- build_sdcard.sh | 2 +- home.admin/_version.info | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build_sdcard.sh b/build_sdcard.sh index d39edf2..a39b14e 100644 --- a/build_sdcard.sh +++ b/build_sdcard.sh @@ -12,7 +12,7 @@ echo "" echo "*****************************************" -echo "* RASPIBLITZ SD CARD IMAGE SETUP v1.00 *" +echo "* RASPIBLITZ SD CARD IMAGE SETUP v1.1 *" echo "*****************************************" echo "" diff --git a/home.admin/_version.info b/home.admin/_version.info index 4b034a1..ba1762e 100644 --- a/home.admin/_version.info +++ b/home.admin/_version.info @@ -1,2 +1,2 @@ # RaspiBlitz Version - always [main].[sub] -codeVersion="1.0" \ No newline at end of file +codeVersion="1.1" \ No newline at end of file From 3756531b0e425532b23a05d6d27ad2aa2fd358e9 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Wed, 13 Mar 2019 18:13:49 +0100 Subject: [PATCH 05/21] #322 install checks on RTL --- home.admin/00settingsMenuServices.sh | 20 ++++++++++----- home.admin/config.scripts/bonus.rtl.sh | 35 ++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/home.admin/00settingsMenuServices.sh b/home.admin/00settingsMenuServices.sh index 843f77b..51f592d 100644 --- a/home.admin/00settingsMenuServices.sh +++ b/home.admin/00settingsMenuServices.sh @@ -172,13 +172,21 @@ if [ "${rtlWebinterface}" != "${choice}" ]; then echo "RTL Webinterface Setting changed .." anychange=1 sudo /home/admin/config.scripts/bonus.rtl.sh ${choice} + errorOnInstall=$? if [ "${choice}" = "on" ]; then - localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/') - l1="RTL web servcie will be ready AFTER NEXT REBOOT:" - l2="Try to open the following URL in your local webrowser" - l3="and login with your PASSWORD B." - l4="---> http://${localip}:3000" - dialog --title 'OK' --msgbox "${l1}\n${l2}\n${l3}\n${l4}" 11 65 + if [ ${errorOnInstall} -eq 0 ]; then + localip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/') + l1="RTL web servcie will be ready AFTER NEXT REBOOT:" + l2="Try to open the following URL in your local webrowser" + l3="and login with your PASSWORD B." + l4="---> http://${localip}:3000" + dialog --title 'OK' --msgbox "${l1}\n${l2}\n${l3}\n${l4}" 11 65 + else + l1="!!! FAIL on RTL install !!!" + l2="Try manual install on terminal after rebootwith:" + l3="sudo /home/admin/config.scripts/bonus.rtl.sh on" + dialog --title 'FAIL' --msgbox "${l1}\n${l2}\n${l3}" 10 65 + fi fi needsReboot=1 else diff --git a/home.admin/config.scripts/bonus.rtl.sh b/home.admin/config.scripts/bonus.rtl.sh index 26781e5..5e2b77d 100755 --- a/home.admin/config.scripts/bonus.rtl.sh +++ b/home.admin/config.scripts/bonus.rtl.sh @@ -29,9 +29,6 @@ sudo systemctl stop RTL 2>/dev/null if [ "$1" = "1" ] || [ "$1" = "on" ]; then echo "*** INSTALL RTL ***" - # setting value in raspi blitz config - sudo sed -i "s/^rtlWebinterface=.*/rtlWebinterface=on/g" /mnt/hdd/raspiblitz.conf - isInstalled=$(sudo ls /etc/systemd/system/RTL.service 2>/dev/null | grep -c 'RTL.service') if [ ${isInstalled} -eq 0 ]; then @@ -42,18 +39,42 @@ if [ "$1" = "1" ] || [ "$1" = "on" ]; then sudo apt-get install -y nodejs echo "" + # check if nodeJS was installed + nodeJSInstalled=$(node -v | grep -c "v11.") + if [ nodeJSInstalled -eq 0 ]; then + echo "FAIL - Was not able to install nodeJS 11" + echo "ABORT - RTL install" + exit 1 + fi + # download source code and set to tag release echo "*** Get the RTL Source Code ***" git clone https://github.com/ShahanaFarooqui/RTL.git cd RTL - git reset --hard v0.2.1 - #git reset --hard v0.1.14-alpha + git reset --hard v0.2.15 + # check if node_modles exists now + if [ -d "/home/admin/RTL" ]; then + echo "OK - RTL code copy looks good" + else + echo "FAIL - code copy did not run correctly" + echo "ABORT - RTL install" + exit 1 + fi + echo "" # install echo "*** Run: npm install ***" npm install cd .. + # check if node_modles exists now + if [ -d "/home/admin/RTL/node_modules" ]; then + echo "OK - RTL install looks good" + else + echo "FAIL - npm install did not run correctly" + echo "ABORT - RTL install" + exit 1 + fi echo "" # prepare RTL.conf file @@ -77,10 +98,14 @@ if [ "$1" = "1" ] || [ "$1" = "on" ]; then sudo sed -i "s|chain/bitcoin/mainnet|chain/${network}/${chain}net|" /etc/systemd/system/RTL.service sudo systemctl enable RTL echo "OK - RTL is now ACTIVE" + else echo "RTL already installed." fi + # setting value in raspi blitz config + sudo sed -i "s/^rtlWebinterface=.*/rtlWebinterface=on/g" /mnt/hdd/raspiblitz.conf + echo "needs reboot to activate new setting" exit 0 fi From 9fc9188081bea5a2affeaeae4cdc30270558c517 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 00:15:19 +0100 Subject: [PATCH 06/21] FAQ info about rounting --- FAQ.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/FAQ.md b/FAQ.md index 7e5f576..21f6ac5 100644 --- a/FAQ.md +++ b/FAQ.md @@ -506,3 +506,12 @@ You could try to re-index, but that can take some very long time - multiple days Another option would be to delete the old blockchain and get a new one. See for details the FAQ question: [I have the full blockchain on another computer. How do I copy it to the RaspiBlitz?](FAQ.md#i-have-the-full-blockchain-on-another-computer-how-do-i-copy-it-to-the-raspiblitz). And even if you are not able to delete the data, first rename the undeletable folders and then follow the instructions. Also make sure to check again on your power supply - it needs to deliver equal or more then 3A and should deliver a stable current. If you think your HDD is degrading - maybe this is a good time to replace it. See for details the FAQ question: [How can I recover my coins from a failing RaspiBlitz?](FAQ.md#how-can-i-recover-my-coins-from-a-failing-raspiblitz) + +## Why is my node not routing? + +1. You don't have inbound liquidity +2. Low uptime +3. Capital is committed to competitive destinations +4. Capital committed to destinations no one wants to send to +5. Fees are too high +6. Your inbound liquidity doesn't have good inbound liquidity itself \ No newline at end of file From 3a3c7d34923d82931634f78f7d5168007a0ed92c Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 10:55:40 +0100 Subject: [PATCH 07/21] #394 emergency delete logs on boot >1GB --- home.admin/_bootstrap.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index 4d4c160..e3dec01 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -58,10 +58,21 @@ if [ "${setupStep}" != "100" ]; then fi sudo chmod 777 ${infoFile} -# cleaning system logs to prevent SD card filling up +# Emergency cleaning logs when over 1GB (to prevent SD card filling up) # see https://github.com/rootzoll/raspiblitz/issues/418#issuecomment-472180944 -echo "Cleaning system logs" -sudo rm /var/log/daemon* && sudo rm /var/log/*.gz +echo "*** Checking Log Size ***" +logsMegaByte=$(sudo du -c -m /var/log | grep "total" | awk '{print $1;}') +if [ ${logsMegaByte} -gt 1000 ]; then + echo "WARN !! Logs /var/log in are bigger then 1GB" + echo "ACTION --> DELETED ALL LOGS" + sudo rm -r /var/log/* + sleep 3 + echo "WARN !! Logs in /var/log in were bigger then 1GB and got emergency delete to prevent fillup." + echo "If you see this in the logs please report to the GitHub issues, so LOG config needs to hbe optimized." +else + echo "OK - logs are at ${logsMegaByte} MB - within safety limit" +fi +echo "" ################################ # GENERATE UNIQUE SSH PUB KEYS From 4dd993ce0d0765e79b7dc0d3edb1982da832ac78 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 11:39:56 +0100 Subject: [PATCH 08/21] #421 added go version check --- home.admin/97addMobileWalletZap.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/home.admin/97addMobileWalletZap.sh b/home.admin/97addMobileWalletZap.sh index 116db47..8054d4a 100755 --- a/home.admin/97addMobileWalletZap.sh +++ b/home.admin/97addMobileWalletZap.sh @@ -15,11 +15,13 @@ if [ ${#GOPATH} -eq 0 ]; then fi # make sure go is installed +goVersion="1.11" +echo "### Check Framework: GO ###" goInstalled=$(go version 2>/dev/null | grep -c 'go') if [ ${goInstalled} -eq 0 ];then - echo "### Installing GO ###" - wget https://storage.googleapis.com/golang/go1.11.linux-armv6l.tar.gz - sudo tar -C /usr/local -xzf go1.11.linux-armv6l.tar.gz + echo "---> Installing GO" + wget https://storage.googleapis.com/golang/go${goVersion}.linux-armv6l.tar.gz + sudo tar -C /usr/local -xzf go${goVersion}.linux-armv6l.tar.gz sudo rm *.gz sudo mkdir /usr/local/gocode sudo chmod 777 /usr/local/gocode @@ -27,11 +29,19 @@ if [ ${goInstalled} -eq 0 ];then fi if [ ${goInstalled} -eq 0 ];then echo "FAIL: Was not able to install GO (needed to run LndConnect)" + sleep 4 exit 1 fi +clear +correctGoVersion=$(go version | grep -c "go${goVersion}") +if [ ${correctGoVersion} -eq 0 ]; then + echo "WARNING: You work with a untested version of GO - should be ${goVersion} .. trying to continue" + go version + sleep 6 + echo "" +fi # make sure qrcode-encoder in installed -clear echo "*** Setup ***" echo "" echo "Installing zapconnect. Please wait..." From d37e49b41a48345d3de405599e6fc2653f7b8567 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 12:07:38 +0100 Subject: [PATCH 09/21] fsck the harddisk on every boot #360 --- home.admin/40addHDD.sh | 7 ++++++- home.admin/97addMobileWalletZap.sh | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/home.admin/40addHDD.sh b/home.admin/40addHDD.sh index 41c418b..7db2ed0 100755 --- a/home.admin/40addHDD.sh +++ b/home.admin/40addHDD.sh @@ -25,7 +25,8 @@ if [ ${existsHDD} -gt 0 ]; then uuid=$1 fstabOK=$(cat /etc/fstab | grep -c ${uuid}) if [ ${fstabOK} -eq 0 ]; then - fstabAdd="UUID=${uuid} /mnt/hdd ext4 noexec,defaults 0 0" + # see https://github.com/rootzoll/raspiblitz/issues/360#issuecomment-467567572 + fstabAdd="UUID=${uuid} /mnt/hdd ext4 noexec,defaults 0 2" echo "Adding line to /etc/fstab ..." echo ${fstabAdd} # adding the new line after line 3 to the /etc/fstab @@ -46,6 +47,10 @@ if [ ${existsHDD} -gt 0 ]; then echo "OK - HDD is mounted" echo "" + # setting fsk check intervall to 1 + # see https://github.com/rootzoll/raspiblitz/issues/360#issuecomment-467567572 + sudo tune2fs -c 1 /dev/sda1 + # init the RASPIBLITZ Config configFile="/mnt/hdd/raspiblitz.conf" configExists=$(sudo ls ${configFile} | grep -c 'raspiblitz.conf') diff --git a/home.admin/97addMobileWalletZap.sh b/home.admin/97addMobileWalletZap.sh index 8054d4a..666cc14 100755 --- a/home.admin/97addMobileWalletZap.sh +++ b/home.admin/97addMobileWalletZap.sh @@ -62,10 +62,10 @@ 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 "1. Install the app 'TestFlight' from Apple Appstore. Open it and agree to all terms of services." +echo "2. Open on your iOS device https://github.com/LN-Zap/zap-iOS and follow 'Download the Alpha'" echo "" -echo "*** STEP 1 ***" +echo "*** PAIRING STEP 1 ***" if [ ${#dynDomain} -eq 0 ]; then echo "Once you have the app is running make sure you are on the same local network (WLAN same as LAN)." fi @@ -77,7 +77,7 @@ echo "Then PRESS ENTER here in the terminal to generare the QR code and scan it read key clear -echo "*** STEP 2 : Click on Scan (make whole QR code fill camera) ***" +echo "*** PAIRING STEP 2 : Click on Scan (make whole QR code fill camera) ***" if [ ${#dynDomain} -eq 0 ]; then # If you drop the -i parameter, lndconnect will use the external IP. From adbd940331b530dc1becedd9e3e44834457f0cc6 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 12:39:16 +0100 Subject: [PATCH 10/21] #195 try to kickstart LCD back with fbi on boot --- build_sdcard.sh | 3 +++ home.admin/_bootstrap.sh | 6 ++++++ pictures/logoraspiblitz.png | Bin 0 -> 49267 bytes 3 files changed, 9 insertions(+) create mode 100644 pictures/logoraspiblitz.png diff --git a/build_sdcard.sh b/build_sdcard.sh index a39b14e..671a637 100644 --- a/build_sdcard.sh +++ b/build_sdcard.sh @@ -184,6 +184,9 @@ sudo apt-get install -y htop git curl bash-completion jq dphys-swapfile # installs bandwidth monitoring for future statistics sudo apt-get install -y vnstat +# preprae for display graphics mode +sudo apt-get install -y fbi + echo "" echo "*** ADDING MAIN USER admin ***" # based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-main-user-admin diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index e3dec01..25aa80f 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -38,6 +38,12 @@ echo "Running RaspiBlitz Bootstrap ${codeVersion}" >> $logFile date >> $logFile echo "***********************************************" >> $logFile +# display 3 secs logo - try to kickstart LCD +# see https://github.com/rootzoll/raspiblitz/issues/195#issuecomment-469918692 +sudo fbi -a -T 1 -d /dev/fb5 --noverbose /home/admin/raspiblitz/pictures/logoraspiblitz.png +sleep 3 +sudo killall -3 fbi + # set default values for raspiblitz.info network="" chain="" diff --git a/pictures/logoraspiblitz.png b/pictures/logoraspiblitz.png new file mode 100644 index 0000000000000000000000000000000000000000..b139cfbb3d99c1c96db0b9337ab9c4ef83dbcf55 GIT binary patch literal 49267 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 Date: Thu, 14 Mar 2019 12:41:58 +0100 Subject: [PATCH 11/21] fix to fbi1 --- home.admin/_bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index 25aa80f..7d436ca 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -40,7 +40,7 @@ echo "***********************************************" >> $logFile # display 3 secs logo - try to kickstart LCD # see https://github.com/rootzoll/raspiblitz/issues/195#issuecomment-469918692 -sudo fbi -a -T 1 -d /dev/fb5 --noverbose /home/admin/raspiblitz/pictures/logoraspiblitz.png +sudo fbi -a -T 1 -d /dev/fb1 --noverbose /home/admin/raspiblitz/pictures/logoraspiblitz.png sleep 3 sudo killall -3 fbi From 92be6ffa2d18fb034b773b190f22bab8298cb641 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 12:47:33 +0100 Subject: [PATCH 12/21] make start boot screen 5 secs --- home.admin/_bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index 7d436ca..d89f857 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -41,7 +41,7 @@ echo "***********************************************" >> $logFile # display 3 secs logo - try to kickstart LCD # see https://github.com/rootzoll/raspiblitz/issues/195#issuecomment-469918692 sudo fbi -a -T 1 -d /dev/fb1 --noverbose /home/admin/raspiblitz/pictures/logoraspiblitz.png -sleep 3 +sleep 5 sudo killall -3 fbi # set default values for raspiblitz.info From 060c670c46d05897b7d6dbac5fb3d7a7213bce81 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:03:11 +0100 Subject: [PATCH 13/21] #88 add redis services --- build_sdcard.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build_sdcard.sh b/build_sdcard.sh index 671a637..c04bef7 100644 --- a/build_sdcard.sh +++ b/build_sdcard.sh @@ -184,9 +184,15 @@ sudo apt-get install -y htop git curl bash-completion jq dphys-swapfile # installs bandwidth monitoring for future statistics sudo apt-get install -y vnstat -# preprae for display graphics mode +# prepare for display graphics mode +# see https://github.com/rootzoll/raspiblitz/pull/334 sudo apt-get install -y fbi +# prepare dor display service +# see https://github.com/rootzoll/raspiblitz/issues/88#issuecomment-471342311 +sudo apt-get install -y redis-server +sudo -H pip3 install redis + echo "" echo "*** ADDING MAIN USER admin ***" # based on https://github.com/Stadicus/guides/blob/master/raspibolt/raspibolt_20_pi.md#adding-main-user-admin From fd235d1d3b136750842918af43fe9da391841baf Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:14:04 +0100 Subject: [PATCH 14/21] EXT4-fs error #428 --- build_sdcard.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_sdcard.sh b/build_sdcard.sh index c04bef7..342da93 100644 --- a/build_sdcard.sh +++ b/build_sdcard.sh @@ -132,6 +132,8 @@ if [ "${baseImage}" = "raspbian" ]; then sudo raspi-config nonint do_boot_wait 0 # set WIFI country so boot does not block sudo raspi-config nonint do_wifi_country US + # see https://github.com/rootzoll/raspiblitz/issues/428#issuecomment-472822840 + echo "max_usb_current=1" | sudo tee -a /boot/config.txt # extra: remove some big packages not needed sudo apt-get remove -y --purge libreoffice* oracle-java* chromium-browser nuscratch scratch sonic-pi minecraft-pi python-pygame sudo apt-get clean From cca314d6702707be73be2c89075a126948285df1 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:18:04 +0100 Subject: [PATCH 15/21] #361 remove FTP option --- FAQ.md | 6 +++--- README.md | 17 ++++------------- home.admin/10setupBlitz.sh | 5 ----- home.admin/50torrentHDD.sh | 2 +- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/FAQ.md b/FAQ.md index 21f6ac5..1719f78 100644 --- a/FAQ.md +++ b/FAQ.md @@ -55,11 +55,11 @@ Of course, people should modify the system, add own scripts, etc ... but if you ## How can I avoid using a prepared blockchain and validate myself? -The torrent and FTP download use a prepared blockchain to kick start the RaspiBlitz. If you want to selft validate you could do this on another more powerful computer and then transfere your own validated blockchain over to the RaspiBlitz. Check the options `Copying from another Computer` & `Cloning from a 2nd HDD` described in the [README](README.md) for more details. +The torrent download use a prepared blockchain to kick start the RaspiBlitz. If you want to selft validate you could do this on another more powerful computer and then transfere your own validated blockchain over to the RaspiBlitz. Check the options `Copying from another Computer` & `Cloning from a 2nd HDD` described in the [README](README.md) for more details. ## I have the full blockchain on another computer. How do I copy it to the RaspiBlitz? -Copying a already synced blockchain from another computer (for example your Laptop) can be a quick way to get the RaspiBlitz started or replacing a corrupted blockchain with a fresh one. Also that way you synced and verified the blockchain yourself and not trusting the RaspiBlitz FTP/Torrent downloads (dont trust, verify). +Copying a already synced blockchain from another computer (for example your Laptop) can be a quick way to get the RaspiBlitz started or replacing a corrupted blockchain with a fresh one. Also that way you synced and verified the blockchain yourself and not trusting the RaspiBlitz Torrent downloads (dont trust, verify). One requirement is that the blockchain is from another bitcoin-core client with version greater or equal to 0.17.1 with transaction index switched on (`txindex=1` in the `bitcoin.conf`). @@ -107,7 +107,7 @@ To connect the 2nd HDD to the RaspiBlitz, the use of a Y cable to provide extra ## Why is my "final sync" taking so long? -First of all if you see a final sync over 90% and you can see from time to time small increase - you should be OK ... this can take some looong time to catch up with the network. Only in the case that you activly choose the `SYNC` option in the `Getting the Blockchain` a final sync under 90% is OK. If you did a torrent, a FTP or a copy from another computer and seeing under 90% somthing went wrong and the setup process is ignoring your prepared Blockchain and doing a full sync - which can almost take forever on a raspberryPi. +First of all if you see a final sync over 90% and you can see from time to time small increase - you should be OK ... this can take some looong time to catch up with the network. Only in the case that you activly choose the `SYNC` option in the `Getting the Blockchain` a final sync under 90% is OK. If you did a torrent or a copy from another computer and seeing under 90% somthing went wrong and the setup process is ignoring your prepared Blockchain and doing a full sync - which can almost take forever on a raspberryPi. So if something is wrong (like mentioned above) then try again from the beginning. You need to reset your HDD for a fresh start: SSH in as admin user. Abort the final sync info with CTRL+c to get to the terminal. There run `sudo /home/admin/XXcleanHDD.sh -all` and follow the script to delete all data in HDD. When finsihed power down with `sudo shutdown now`. Then make a fresh SD card from image and this time try another option to get the blockchain. If you run into trouble the second time, please report an issue on GitHub. diff --git a/README.md b/README.md index 5efad32..577d616 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ This is the default way to download the blockchain data for the RaspiBlitz. If y ![DOWNLOAD1](pictures/download-torrent.png) -*This can take a while - normally it should be done if you keep it running over night, but some users reported that it took up to 3 days. If it takes longer than that or you cannot see any progress (downloading starting) for over an hour after you started thsi option consider to cancel the download and choose the FTP download option.* +*This can take a while - normally it should be done if you keep it running over night, but some users reported that it took up to 3 days. If it takes longer than that or you cannot see any progress (downloading starting) for over an hour after you started this option consider to cancel the download and go with the COPY option or retry fresh.* It is safe to close the terminal window (shutdown your laptop) while the RaspiBlitz is doing the torrent download. To check on progress and to continue the setup you need to ssh back in again. @@ -197,17 +197,8 @@ You can cancel the torrent download by keeping the key `x` pressed. Then the dow * [How can I avoid using a prepared blockchain and validate myself?](FAQ.md#how-can-i-avoid-using-a-prepared-blockchain-and-validate-myself) * [Why is taking my torrent download of the blockchain so long?]() -#### 2. FTP-Download -You should try the FTP download if the torrent option is not working for you. Please be aware that the files are hosted on a central server by us and up-time and bandwidth is not guaranteed. If you start it, you should see the following screen: - -![DOWNLOAD1](pictures/download-ftp.png) - -It is safe to close the terminal window (shutdown your laptop) while the RaspiBlitz is doing the FTP download. To check on progress and to continue the setup you need to ssh back in again. - -You can cancel the FTP download by keeping the key `x` pressed. Then the download will stop and you will be asked if you want to keep the progress so far. This makes sense if you need to shutdown the RaspiBlitz and you want to continue later or when you want to try another download option but want to keep the option to continue on FTP download if the other option is slower or not working. - -#### 4. Copying from another Computer +#### 2. Copying from another Computer If you have another computer available (laptop, desktop or another raspiblitz) that already runs a working blockchain (with txindex=1) you can use this option to copy it over to the RaspiBlitz. This will be done over the local network by SCP (SSH file transfere). Choose this option and follow the given instructions. @@ -215,11 +206,11 @@ This is also the best option if you dont like to run your RaspiBlitz with a prep More details: [I have the full blockchain on another computer. How do I copy it to the RaspiBlitz?](FAQ.md#i-have-the-full-blockchain-on-another-computer-how-do-i-copy-it-to-the-raspiblitz) -#### 5. Cloning from a 2nd HDD +#### 3. Cloning from a 2nd HDD If is a backup way to transfere a blockchain from another computer if copying over the network is not working. More details on the setup can be found [here](FAQ.md#how-do-i-clone-the-blockchain-from-a-2nd-hdd). -#### 6. Sync from Bitcoin-Network +#### 4. Sync from Bitcoin-Network This is the fallback of last resort. A RaspberryPi has a very low power CPU and syncing+validating the blockchain directly with the peer2peer network can take multiple weeks - thats why the other options above where invented. diff --git a/home.admin/10setupBlitz.sh b/home.admin/10setupBlitz.sh index 6eb5ad1..d17206f 100755 --- a/home.admin/10setupBlitz.sh +++ b/home.admin/10setupBlitz.sh @@ -188,7 +188,6 @@ if [ ${mountOK} -eq 1 ]; then menuitem=$(dialog --clear --beep --backtitle "RaspiBlitz" --title "Getting the Blockchain" \ --menu "You need a copy of the Bitcoin Blockchain - you have 5 options:" 13 75 5 \ T "TORRENT --> MAINNET + TESTNET thru Torrent (DEFAULT)" \ - D "DOWNLOAD --> MAINNET + TESTNET per FTP (FALLBACK)" \ C "COPY --> BLOCKCHAINDATA from another node with SCP" \ N "CLONE --> BLOCKCHAINDATA from 2nd HDD (extra cable)"\ S "SYNC --> MAINNET thru Bitcoin Network (ULTRA SLOW)" 2>&1 >/dev/tty) @@ -199,7 +198,6 @@ if [ ${mountOK} -eq 1 ]; then menuitem=$(dialog --clear --beep --backtitle "RaspiBlitz" --title "Getting the Blockchain" \ --menu "You need a copy of the Litecoin Blockchain - you have 3 options:" 13 75 4 \ T "TORRENT --> MAINNET thru Torrent (DEFAULT)" \ - D "DOWNLOAD --> MAINNET per FTP (FALLBACK)" \ S "SYNC --> MAINNET thru Litecoin Network (FALLBACK+SLOW)" 2>&1 >/dev/tty) # error @@ -225,9 +223,6 @@ if [ ${mountOK} -eq 1 ]; then S) /home/admin/50syncHDD.sh ;; - D) - /home/admin/50downloadHDD.sh - ;; esac exit 1 diff --git a/home.admin/50torrentHDD.sh b/home.admin/50torrentHDD.sh index bc44b5b..9e8d731 100755 --- a/home.admin/50torrentHDD.sh +++ b/home.admin/50torrentHDD.sh @@ -245,7 +245,7 @@ if [ ${torrentError} -gt 0 ]; then # User Cancel --> Torrent incomplete sleep 3 echo -ne '\007' - dialog --title " WARNING (${torrentError})" --yesno "The Torrent download failed or is not complete - maybe try FTP download next time. Do you want keep already downloaded torrent data?" 8 57 + dialog --title " WARNING (${torrentError})" --yesno "The Torrent download failed or is not complete - maybe try COPY option. Do you want keep already downloaded torrent data?" 8 57 response=$? case $response in 1) sudo rm -rf ${targetDir}; sudo rm -rf ${sessionDir} ;; From 14ebd783a97c27111d26539d5d15e190a3339100 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:23:12 +0100 Subject: [PATCH 16/21] #358 torrent info --- FAQ.md | 4 ++++ README.md | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 1719f78..b6043b1 100644 --- a/FAQ.md +++ b/FAQ.md @@ -343,6 +343,10 @@ The downloaded blockchain is pre-indexed and pre-validated. That should be pract If you dont trust the download or you want to run the RaspiBlitz in a more production like setup (on your own risk) then don't use the torrent/ftp download and choose the option to COPY the blockchain data from a more powerful computer (laptop or desktop) where you synced, verified and indexed the blockchain all by your yourself - see [README](README.md#4-copying-from-another-computer) for more details. +# Why is taking my torrent download of the blockchain so long? + +Other users reported that opening up and forwarding the following port from their router to the RaspiBlitz helped speed up and getting started the torrent download: 6881-6889, 6969, 1337 + ## What is the "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. diff --git a/README.md b/README.md index 577d616..d0bcaed 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,6 @@ It is safe to close the terminal window (shutdown your laptop) while the RaspiBl You can cancel the torrent download by keeping the key `x` pressed. Then the download will stop and you will be asked if you want to keep the progress so far. This makes sense if you need to shutdown the RaspiBlitz and you want to continue later or when you want to try another download option but want to keep the option to continue on torrent if the other option is slower or not working. * [How can I avoid using a prepared blockchain and validate myself?](FAQ.md#how-can-i-avoid-using-a-prepared-blockchain-and-validate-myself) -* [Why is taking my torrent download of the blockchain so long?]() #### 2. Copying from another Computer From 17ee2be2c9623772c8dec98d729aa438fcb47de7 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:24:32 +0100 Subject: [PATCH 17/21] #358 README torrent update --- FAQ.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index b6043b1..2d3fead 100644 --- a/FAQ.md +++ b/FAQ.md @@ -343,7 +343,7 @@ The downloaded blockchain is pre-indexed and pre-validated. That should be pract If you dont trust the download or you want to run the RaspiBlitz in a more production like setup (on your own risk) then don't use the torrent/ftp download and choose the option to COPY the blockchain data from a more powerful computer (laptop or desktop) where you synced, verified and indexed the blockchain all by your yourself - see [README](README.md#4-copying-from-another-computer) for more details. -# Why is taking my torrent download of the blockchain so long? +## Why is taking my torrent download of the blockchain so long? Other users reported that opening up and forwarding the following port from their router to the RaspiBlitz helped speed up and getting started the torrent download: 6881-6889, 6969, 1337 diff --git a/README.md b/README.md index d0bcaed..51d7c1b 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,7 @@ It is safe to close the terminal window (shutdown your laptop) while the RaspiBl You can cancel the torrent download by keeping the key `x` pressed. Then the download will stop and you will be asked if you want to keep the progress so far. This makes sense if you need to shutdown the RaspiBlitz and you want to continue later or when you want to try another download option but want to keep the option to continue on torrent if the other option is slower or not working. * [How can I avoid using a prepared blockchain and validate myself?](FAQ.md#how-can-i-avoid-using-a-prepared-blockchain-and-validate-myself) +* [Why is taking my torrent download of the blockchain so long?](FAQ.md#why-is-taking-my-torrent-download-of-the-blockchain-so-long) #### 2. Copying from another Computer From 7d08df57567f43435b61c0885378d9cc4c8290ba Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 13:54:45 +0100 Subject: [PATCH 18/21] #371 sanity check on publicIP data --- home.admin/_background.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/home.admin/_background.sh b/home.admin/_background.sh index 21d2153..1dfd347 100644 --- a/home.admin/_background.sh +++ b/home.admin/_background.sh @@ -80,6 +80,18 @@ do echo "freshPublicIP(${freshPublicIP})" echo "publicIP(${publicIP})" + # sanity check on IP data + # see https://github.com/rootzoll/raspiblitz/issues/371#issuecomment-472416349 + echo "-> sanity check of IP data: ${freshPublicIP}" + if [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then + echo "OK IPv4" + elif [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + echo "OK IPv6" + else + echo "FAIL - not an IPv4 or IPv6 address" + freshPublicIP="" + fi + if [ ${#freshPublicIP} -eq 0 ]; then echo "freshPublicIP is ZERO - ignoring" From 1b908303b574c85d784099044976d2b0512e1a72 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 14:05:37 +0100 Subject: [PATCH 19/21] test ip sanaity check --- home.admin/_background.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home.admin/_background.sh b/home.admin/_background.sh index 1dfd347..2566856 100644 --- a/home.admin/_background.sh +++ b/home.admin/_background.sh @@ -67,7 +67,7 @@ do # every 15min - not too often # because its a ping to external service - recheckPublicIP=$((($counter % 900)+1)) + recheckPublicIP=$((($counter % 9)+1)) updateDynDomain=0 if [ ${recheckPublicIP} -eq 1 ]; then echo "*** RECHECK PUBLIC IP ***" From f80e32b95d5999185f4bcbf3da0cbe0a8b355ac2 Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 14:08:20 +0100 Subject: [PATCH 20/21] test IP sanity check --- home.admin/_background.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/home.admin/_background.sh b/home.admin/_background.sh index 2566856..5e9d8d2 100644 --- a/home.admin/_background.sh +++ b/home.admin/_background.sh @@ -83,10 +83,10 @@ do # sanity check on IP data # see https://github.com/rootzoll/raspiblitz/issues/371#issuecomment-472416349 echo "-> sanity check of IP data: ${freshPublicIP}" - if [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then - echo "OK IPv4" - elif [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + if [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then echo "OK IPv6" + elif [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then + echo "OK IPv4" else echo "FAIL - not an IPv4 or IPv6 address" freshPublicIP="" From e9efa4bd65fddedce4d17c79b46216f68f4dcbcc Mon Sep 17 00:00:00 2001 From: Christian Rotzoll Date: Thu, 14 Mar 2019 14:19:00 +0100 Subject: [PATCH 21/21] #371 sanaity check on public ip data --- home.admin/40addHDD.sh | 13 +++++++++++++ home.admin/_background.sh | 2 +- home.admin/_bootstrap.sh | 13 +++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/home.admin/40addHDD.sh b/home.admin/40addHDD.sh index 7db2ed0..864e92a 100755 --- a/home.admin/40addHDD.sh +++ b/home.admin/40addHDD.sh @@ -69,6 +69,19 @@ if [ ${existsHDD} -gt 0 ]; then # try to determine publicIP and if not possible use localIP as placeholder # https://github.com/rootzoll/raspiblitz/issues/312#issuecomment-462675101 freshPublicIP=$(curl -s http://v4.ipv6-test.com/api/myip.php) + + # sanity check on IP data + # see https://github.com/rootzoll/raspiblitz/issues/371#issuecomment-472416349 + echo "-> sanity check of IP data: ${freshPublicIP}" + if [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + echo "OK IPv6" + elif [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then + echo "OK IPv4" + else + echo "FAIL - not an IPv4 or IPv6 address" + freshPublicIP="" + fi + if [ ${#freshPublicIP} -eq 0 ]; then localIP=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/') echo "WARNING: No publicIP information at all yet - working with placeholder : ${localIP}" diff --git a/home.admin/_background.sh b/home.admin/_background.sh index 5e9d8d2..f9310a1 100644 --- a/home.admin/_background.sh +++ b/home.admin/_background.sh @@ -67,7 +67,7 @@ do # every 15min - not too often # because its a ping to external service - recheckPublicIP=$((($counter % 9)+1)) + recheckPublicIP=$((($counter % 900)+1)) updateDynDomain=0 if [ ${recheckPublicIP} -eq 1 ]; then echo "*** RECHECK PUBLIC IP ***" diff --git a/home.admin/_bootstrap.sh b/home.admin/_bootstrap.sh index d89f857..456b5eb 100644 --- a/home.admin/_bootstrap.sh +++ b/home.admin/_bootstrap.sh @@ -310,6 +310,19 @@ if [ ${configExists} -eq 1 ]; then # wait otherwise looking for publicIP fails sleep 5 freshPublicIP=$(curl -s http://v4.ipv6-test.com/api/myip.php) + + # sanity check on IP data + # see https://github.com/rootzoll/raspiblitz/issues/371#issuecomment-472416349 + echo "-> sanity check of IP data: ${freshPublicIP}" + if [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + echo "OK IPv6" + elif [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then + echo "OK IPv4" + else + echo "FAIL - not an IPv4 or IPv6 address" + freshPublicIP="" + fi + if [ ${#freshPublicIP} -eq 0 ]; then # prevent having no publicIP set at all and LND getting stuck # https://github.com/rootzoll/raspiblitz/issues/312#issuecomment-462675101