2019-06-15 23:02:44 +00:00
import copy
import requests
import subprocess
import os
import time
import re
2020-11-08 21:13:03 +00:00
from flask import current_app as app
2019-06-15 23:02:44 +00:00
from threading import Timer
from bitcoin_info import *
2020-10-08 02:59:44 +00:00
from systemctl_info import *
2019-06-15 23:02:44 +00:00
# Variables
lightning_info = None
lnd_ready = False
2019-08-01 04:27:16 +00:00
lnd_version = None
2020-08-21 00:56:31 +00:00
loopd_version = None
2020-01-30 03:00:02 +00:00
lightning_peers = None
lightning_channels = None
lightning_channel_balance = None
lightning_wallet_balance = None
2020-05-19 02:43:43 +00:00
lightning_desync_count = 0
2019-06-15 23:02:44 +00:00
LND_FOLDER = " /mnt/hdd/mynode/lnd/ "
MACAROON_FILE = " /mnt/hdd/mynode/lnd/data/chain/bitcoin/mainnet/admin.macaroon "
WALLET_FILE = " /mnt/hdd/mynode/lnd/data/chain/bitcoin/mainnet/wallet.db "
TLS_CERT_FILE = " /mnt/hdd/mynode/lnd/tls.cert "
CHANNEL_BACKUP_FILE = " /home/bitcoin/lnd_backup/channel.backup "
LND_REST_PORT = " 10080 "
# Functions
def update_lightning_info ( ) :
global lightning_info
2020-01-30 03:00:02 +00:00
global lightning_peers
global lightning_channels
global lightning_channel_balance
global lightning_wallet_balance
2020-05-19 02:43:43 +00:00
global lightning_desync_count
2019-06-15 23:02:44 +00:00
global lnd_ready
# Get latest LN info
lightning_info = lnd_get ( " /getinfo " )
# Set is LND ready
if lightning_info != None and " synced_to_chain " in lightning_info and lightning_info [ ' synced_to_chain ' ] :
lnd_ready = True
# Check for LND de-sync (this can happen unfortunately)
# See https://github.com/lightningnetwork/lnd/issues/1909
# See https://github.com/bitcoin/bitcoin/pull/14687
# Hopefully patch comes soon to enable TCP keepalive to prevent this from happening
if lnd_ready and lightning_info != None and " synced_to_chain " in lightning_info and not lightning_info [ ' synced_to_chain ' ] :
2020-05-19 02:43:43 +00:00
lightning_desync_count + = 1
os . system ( " printf \" %s | LND De-sync!!! Count: {} \\ n \" \" $(date) \" >> /tmp/lnd_failures " . format ( lightning_desync_count ) )
if lightning_desync_count > = 8 :
os . system ( " printf \" %s | De-sync count too high! Retarting LND... \\ n \" \" $(date) \" >> /tmp/lnd_failures " )
restart_lnd ( )
lightning_desync_count = 0
2020-01-30 03:00:02 +00:00
return True
if lnd_ready :
2020-05-19 02:43:43 +00:00
if lightning_desync_count > 0 :
os . system ( " printf \" %s | De-sync greater than 0 (was {} ), but now synced! Setting to 0. \\ n \" \" $(date) \" >> /tmp/lnd_failures " . format ( lightning_desync_count ) )
lightning_desync_count = 0
2020-01-30 03:00:02 +00:00
lightning_peers = lnd_get ( " /peers " )
lightning_channels = lnd_get ( " /channels " )
lightning_channel_balance = lnd_get ( " /balance/channels " )
lightning_wallet_balance = lnd_get ( " /balance/blockchain " )
2019-06-15 23:02:44 +00:00
return True
2020-01-30 03:00:02 +00:00
def get_new_deposit_address ( ) :
address = " NEW_ADDR "
try :
addressdata = lnd_get ( " /newaddress " )
address = addressdata [ " address " ]
except :
address = " ERROR "
return address
2019-06-15 23:02:44 +00:00
def get_lightning_info ( ) :
global lightning_info
return copy . deepcopy ( lightning_info )
2020-01-30 03:00:02 +00:00
def get_lightning_peers ( ) :
global lightning_peers
return copy . deepcopy ( lightning_peers )
2020-10-01 00:55:24 +00:00
def get_lightning_peer_count ( ) :
info = get_lightning_info ( )
num_peers = 0
2020-10-08 02:59:44 +00:00
if info != None and " num_peers " in info :
2020-10-01 00:55:24 +00:00
num_peers = info [ ' num_peers ' ]
return num_peers
2020-01-30 03:00:02 +00:00
def get_lightning_channels ( ) :
global lightning_channels
return copy . deepcopy ( lightning_channels )
2020-10-01 00:55:24 +00:00
def get_lightning_channel_count ( ) :
channeldata = get_lightning_channels ( )
if channeldata != None and " channels " in channeldata :
return len ( channeldata [ " channels " ] )
return 0
2020-01-30 03:00:02 +00:00
def get_lightning_channel_balance ( ) :
global lightning_channel_balance
return copy . deepcopy ( lightning_channel_balance )
def get_lightning_wallet_balance ( ) :
global lightning_wallet_balance
return copy . deepcopy ( lightning_wallet_balance )
2020-10-01 00:55:24 +00:00
def get_lightning_balance_info ( ) :
channel_balance_data = get_lightning_channel_balance ( )
wallet_balance_data = get_lightning_wallet_balance ( )
balance_data = { }
balance_data [ " channel_balance " ] = " N/A "
balance_data [ " channel_pending " ] = " N/A "
balance_data [ " wallet_balance " ] = " N/A "
balance_data [ " wallet_pending " ] = " N/A "
channel_balance_data = get_lightning_channel_balance ( )
if channel_balance_data != None and " balance " in channel_balance_data :
balance_data [ " channel_balance " ] = channel_balance_data [ " balance " ]
if channel_balance_data != None and " pending_open_balance " in channel_balance_data :
balance_data [ " channel_pending " ] = channel_balance_data [ " pending_open_balance " ]
wallet_balance_data = get_lightning_wallet_balance ( )
if wallet_balance_data != None and " confirmed_balance " in wallet_balance_data :
balance_data [ " wallet_balance " ] = wallet_balance_data [ " confirmed_balance " ]
if wallet_balance_data != None and " unconfirmed_balance " in wallet_balance_data :
balance_data [ " wallet_pending " ] = wallet_balance_data [ " unconfirmed_balance " ]
return balance_data
2019-06-15 23:02:44 +00:00
def is_lnd_ready ( ) :
global lnd_ready
return lnd_ready
def lnd_get ( path ) :
try :
macaroon = get_macaroon ( )
headers = { " Grpc-Metadata-macaroon " : macaroon }
r = requests . get ( " https://localhost: " + LND_REST_PORT + " /v1 " + path , verify = TLS_CERT_FILE , headers = headers )
except Exception as e :
2020-11-08 21:13:03 +00:00
app . logger . info ( " ERROR in lnd_get: " + str ( e ) )
return { " error " : str ( e ) }
2019-06-15 23:02:44 +00:00
return r . json ( )
def gen_new_wallet_seed ( ) :
seed = subprocess . check_output ( " python3 /usr/bin/gen_seed.py " , shell = True )
return seed
def restart_lnd_actual ( ) :
2019-07-03 21:24:01 +00:00
global lnd_ready
lnd_ready = False
2019-06-15 23:02:44 +00:00
os . system ( " systemctl restart lnd " )
os . system ( " systemctl restart lnd_unlock " )
2019-11-04 04:50:47 +00:00
os . system ( " systemctl restart lnd_admin " )
2019-06-15 23:02:44 +00:00
def restart_lnd ( ) :
t = Timer ( 1.0 , restart_lnd_actual )
t . start ( )
def get_macaroon ( ) :
m = subprocess . check_output ( " xxd -ps -u -c 1000 " + MACAROON_FILE , shell = True )
return m . strip ( )
def lnd_wallet_exists ( ) :
return os . path . isfile ( WALLET_FILE )
def create_wallet ( seed ) :
try :
subprocess . check_call ( " create_lnd_wallet.tcl \" " + seed + " \" " , shell = True )
# Sync FS and sleep so the success redirect understands the wallet was created
os . system ( " sync " )
time . sleep ( 2 )
return True
except :
return False
def is_lnd_logged_in ( ) :
try :
macaroon = get_macaroon ( )
headers = { " Grpc-Metadata-macaroon " : macaroon }
r = requests . get ( " https://localhost: " + LND_REST_PORT + " /v1/getinfo " , verify = TLS_CERT_FILE , headers = headers )
if r . status_code == 200 and r . json ( ) :
return True
return False
except :
return False
def lnd_channel_backup_exists ( ) :
return os . path . isfile ( CHANNEL_BACKUP_FILE )
def get_lnd_status ( ) :
if not lnd_wallet_exists ( ) :
return " Please create wallet... "
2020-10-03 04:43:30 +00:00
if not is_bitcoind_synced ( ) :
return " Waiting... "
2019-06-15 23:02:44 +00:00
if is_lnd_ready ( ) :
return " Running "
2019-09-25 01:21:33 +00:00
try :
2020-05-01 17:48:31 +00:00
#log = subprocess.check_output("tail -n 100 /var/log/lnd.log", shell=True)
log = get_journalctl_log ( " lnd " )
2019-09-25 01:21:33 +00:00
lines = log . splitlines ( )
2020-05-01 17:48:31 +00:00
#lines.reverse()
2019-09-25 01:21:33 +00:00
for line in lines :
if " Caught up to height " in line :
m = re . search ( " height ([0-9]+) " , line )
height = m . group ( 1 )
percent = 100.0 * ( float ( height ) / bitcoin_block_height )
return " Syncing... {:.2f} % " . format ( percent )
elif " Waiting for chain backend to finish sync " in line :
return " Syncing... "
elif " Started rescan from block " in line :
return " Scanning... "
2020-05-09 03:35:44 +00:00
elif " Version: " in line :
return " Launching... "
elif " Opening the main database " in line :
return " Opening DB... "
elif " Database now open " in line :
return " DB open... "
elif " Waiting for wallet encryption password " in line :
return " Logging in... "
elif " LightningWallet opened " in line :
return " Wallet open... "
2019-09-25 01:21:33 +00:00
return " Waiting... "
except :
return " Status Error "
2020-10-03 04:43:30 +00:00
def get_lnd_status_color ( ) :
if not is_bitcoind_synced ( ) :
return " yellow "
if not lnd_wallet_exists ( ) :
# This hides the restart /login attempt LND does from the GUI
return " green "
lnd_status_code = get_service_status_code ( " lnd " )
if lnd_status_code != 0 :
lnd_status_color = " red "
lnd_status = get_lnd_status ( )
if lnd_status == " Logging in... " :
lnd_status_color = " yellow "
return " green "
2019-06-15 23:02:44 +00:00
def get_lnd_channels ( ) :
try :
macaroon = get_macaroon ( )
headers = { " Grpc-Metadata-macaroon " : macaroon }
r = requests . get ( " https://localhost: " + LND_REST_PORT + " /v1/channels " , verify = TLS_CERT_FILE , headers = headers )
if r . status_code == 200 and r . json ( ) :
data = r . json ( )
return data [ " channels " ]
return False
except Exception as e :
print ( " EXCEPTION: {} " . format ( str ( e ) ) )
return False
2019-08-01 04:27:16 +00:00
def get_lnd_version ( ) :
global lnd_version
if lnd_version == None :
lnd_version = subprocess . check_output ( " lnd --version | egrep -o ' [0-9]+ \\ .[0-9]+ \\ .[0-9]+ ' | head -n 1 " , shell = True )
2020-10-01 00:55:24 +00:00
return " v {} " . format ( lnd_version )
2019-11-04 04:50:47 +00:00
2020-08-21 00:56:31 +00:00
def get_loopd_version ( ) :
global loopd_version
if loopd_version == None :
loopd_version = subprocess . check_output ( " loopd --version | egrep -o ' [0-9]+ \\ .[0-9]+ \\ .[0-9]+ ' | head -n 1 " , shell = True )
2020-10-01 00:55:24 +00:00
return " v {} " . format ( loopd_version )
2020-08-21 00:56:31 +00:00
2019-11-04 04:50:47 +00:00
def get_default_lnd_config ( ) :
try :
with open ( " /usr/share/mynode/lnd.conf " ) as f :
return f . read ( )
except :
return " ERROR "
def get_lnd_config ( ) :
try :
with open ( " /mnt/hdd/mynode/lnd/lnd.conf " ) as f :
return f . read ( )
except :
return " ERROR "
2019-11-05 01:34:19 +00:00
def get_lnd_custom_config ( ) :
2019-11-04 04:50:47 +00:00
try :
2019-11-05 01:34:19 +00:00
with open ( " /mnt/hdd/mynode/settings/lnd_custom.conf " ) as f :
2019-11-04 04:50:47 +00:00
return f . read ( )
except :
return " ERROR "
2019-11-05 01:34:19 +00:00
def set_lnd_custom_config ( config ) :
2019-11-04 04:50:47 +00:00
try :
2019-11-05 01:34:19 +00:00
with open ( " /mnt/hdd/mynode/settings/lnd_custom.conf " , " w " ) as f :
2019-11-04 04:50:47 +00:00
f . write ( config )
os . system ( " sync " )
return True
except :
return False
2019-11-05 01:34:19 +00:00
2020-07-14 03:10:06 +00:00
def using_lnd_custom_config ( ) :
return os . path . isfile ( " /mnt/hdd/mynode/settings/lnd_custom.conf " )
2019-11-05 01:34:19 +00:00
def delete_lnd_custom_config ( ) :
2020-05-20 03:01:14 +00:00
os . system ( " rm -f /mnt/hdd/mynode/settings/lnd_custom.conf " )
def get_lnd_alias_file_data ( ) :
try :
with open ( " /mnt/hdd/mynode/settings/.lndalias " , " r " ) as f :
return f . read ( ) . strip ( )
except :
return " ERROR "
return " ERROR "