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
2021-03-13 21:57:23 +00:00
from utilities import *
2019-06-15 23:02:44 +00:00
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
2021-01-16 18:15:52 +00:00
loop_version = None
pool_version = None
2020-01-30 03:00:02 +00:00
lightning_peers = None
2021-03-09 03:55:39 +00:00
lightning_peer_aliases = { }
2020-01-30 03:00:02 +00:00
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/ "
TLS_CERT_FILE = " /mnt/hdd/mynode/lnd/tls.cert "
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
2021-03-17 04:31:46 +00:00
def get_lnd_deposit_address ( ) :
2021-03-13 21:57:23 +00:00
if os . path . isfile ( " /tmp/lnd_deposit_address " ) :
return get_file_contents ( " /tmp/lnd_deposit_address " )
2021-03-17 04:31:46 +00:00
return get_new_lnd_deposit_address ( )
2021-03-13 21:57:23 +00:00
2021-03-17 04:31:46 +00:00
def get_new_lnd_deposit_address ( ) :
2020-01-30 03:00:02 +00:00
address = " NEW_ADDR "
try :
addressdata = lnd_get ( " /newaddress " )
address = addressdata [ " address " ]
2021-03-13 21:57:23 +00:00
set_file_contents ( " /tmp/lnd_deposit_address " , address )
2020-01-30 03:00:02 +00:00
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
2021-03-17 04:31:46 +00:00
peerdata = copy . deepcopy ( lightning_peers )
peers = [ ]
if peerdata != None and " peers " in peerdata :
for p in peerdata [ " peers " ] :
peer = p
if " bytes_recv " in p :
peer [ " bytes_recv " ] = " {:.2f} " . format ( float ( p [ " bytes_recv " ] ) / 1000 / 1000 )
else :
peer [ " bytes_recv " ] = " N/A "
if " bytes_sent " in p :
peer [ " bytes_sent " ] = " {:.2f} " . format ( float ( p [ " bytes_sent " ] ) / 1000 / 1000 )
else :
peer [ " bytes_sent " ] = " N/A "
if " sat_sent " in p :
peer [ " sat_sent " ] = format_sat_amount ( peer [ " sat_sent " ] )
if " sat_recv " in p :
peer [ " sat_recv " ] = format_sat_amount ( peer [ " sat_recv " ] )
if " ping_time " not in p :
peer [ " ping_time " ] = " N/A "
if " pub_key " in p :
peer [ " alias " ] = get_lightning_peer_alias ( p [ " pub_key " ] )
else :
peer [ " alias " ] = " Unknown "
peers . append ( peer )
return peers
2020-01-30 03:00:02 +00:00
2021-03-09 03:55:39 +00:00
def get_lightning_node_info ( pubkey ) :
nodeinfo = lnd_get ( " /graph/node/ {} " . format ( pubkey ) , timeout = 2 )
return nodeinfo
def get_lightning_peer_alias ( pubkey ) :
global lightning_peer_aliases
if pubkey in lightning_peer_aliases :
return lightning_peer_aliases [ pubkey ]
nodeinfo = get_lightning_node_info ( pubkey )
if nodeinfo != None and " node " in nodeinfo :
if " alias " in nodeinfo [ " node " ] :
lightning_peer_aliases [ pubkey ] = nodeinfo [ " node " ] [ " alias " ]
return nodeinfo [ " node " ] [ " alias " ]
return " UNKNOWN "
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
2021-03-17 04:31:46 +00:00
channeldata = copy . deepcopy ( lightning_channels )
channels = [ ]
if channeldata != None and " channels " in channeldata :
for c in channeldata [ " channels " ] :
channel = c
if " capacity " in channel :
channel [ " capacity " ] = format_sat_amount ( channel [ " capacity " ] )
else :
channel [ " capacity " ] = " N/A "
if " local_balance " in channel and " remote_balance " in channel :
l = float ( channel [ " local_balance " ] )
r = float ( channel [ " remote_balance " ] )
channel [ " chan_percent " ] = ( l / ( l + r ) ) * 100
else :
channel [ " chan_percent " ] = " 0 "
if " local_balance " in channel :
channel [ " local_balance " ] = format_sat_amount ( channel [ " local_balance " ] )
else :
channel [ " local_balance " ] = " 0 "
if " remote_balance " in channel :
channel [ " remote_balance " ] = format_sat_amount ( channel [ " remote_balance " ] )
else :
channel [ " remote_balance " ] = " 0 "
if " remote_pubkey " in channel :
channel [ " remote_alias " ] = get_lightning_peer_alias ( channel [ " remote_pubkey " ] )
else :
channel [ " remote_alias " ] = " Unknown "
channels . append ( channel )
return channels
2020-01-30 03:00:02 +00:00
2020-10-01 00:55:24 +00:00
def get_lightning_channel_count ( ) :
2021-03-17 04:31:46 +00:00
channels = get_lightning_channels ( )
return len ( channels )
2020-10-01 00:55:24 +00:00
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 :
2021-03-15 04:58:40 +00:00
balance_data [ " channel_balance " ] = format_sat_amount ( channel_balance_data [ " balance " ] )
2020-10-01 00:55:24 +00:00
if channel_balance_data != None and " pending_open_balance " in channel_balance_data :
2021-03-15 04:58:40 +00:00
balance_data [ " channel_pending " ] = format_sat_amount ( channel_balance_data [ " pending_open_balance " ] )
2020-10-01 00:55:24 +00:00
wallet_balance_data = get_lightning_wallet_balance ( )
if wallet_balance_data != None and " confirmed_balance " in wallet_balance_data :
2021-03-15 04:58:40 +00:00
balance_data [ " wallet_balance " ] = format_sat_amount ( wallet_balance_data [ " confirmed_balance " ] )
2020-10-01 00:55:24 +00:00
if wallet_balance_data != None and " unconfirmed_balance " in wallet_balance_data :
2021-03-15 04:58:40 +00:00
balance_data [ " wallet_pending " ] = format_sat_amount ( wallet_balance_data [ " unconfirmed_balance " ] )
2020-10-01 00:55:24 +00:00
return balance_data
2019-06-15 23:02:44 +00:00
def is_lnd_ready ( ) :
global lnd_ready
return lnd_ready
2021-03-09 03:55:39 +00:00
def lnd_get ( path , timeout = 10 ) :
2019-06-15 23:02:44 +00:00
try :
macaroon = get_macaroon ( )
headers = { " Grpc-Metadata-macaroon " : macaroon }
2021-03-09 03:55:39 +00:00
r = requests . get ( " https://localhost: " + LND_REST_PORT + " /v1 " + path , verify = TLS_CERT_FILE , headers = headers , timeout = timeout )
2019-06-15 23:02:44 +00:00
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 ( )
2021-03-09 03:55:39 +00:00
def is_testnet_enabled ( ) :
return os . path . isfile ( " /mnt/hdd/mynode/settings/.testnet_enabled " )
def get_lightning_wallet_file ( ) :
if is_testnet_enabled ( ) :
return " /mnt/hdd/mynode/lnd/data/chain/bitcoin/testnet/wallet.db "
return " /mnt/hdd/mynode/lnd/data/chain/bitcoin/mainnet/wallet.db "
def get_lightning_macaroon_file ( ) :
if is_testnet_enabled ( ) :
return " /mnt/hdd/mynode/lnd/data/chain/bitcoin/testnet/admin.macaroon "
return " /mnt/hdd/mynode/lnd/data/chain/bitcoin/mainnet/admin.macaroon "
2019-06-15 23:02:44 +00:00
def get_macaroon ( ) :
2021-03-09 03:55:39 +00:00
m = subprocess . check_output ( " xxd -ps -u -c 1000 " + get_lightning_macaroon_file ( ) , shell = True )
2019-06-15 23:02:44 +00:00
return m . strip ( )
def lnd_wallet_exists ( ) :
2021-03-09 03:55:39 +00:00
return os . path . isfile ( get_lightning_wallet_file ( ) )
2019-06-15 23:02:44 +00:00
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
2021-03-09 03:55:39 +00:00
def get_lnd_channel_backup_file ( ) :
if is_testnet_enabled ( ) :
return " /home/bitcoin/lnd_backup/channel_testnet.backup "
return " /home/bitcoin/lnd_backup/channel.backup "
2019-06-15 23:02:44 +00:00
def lnd_channel_backup_exists ( ) :
2021-03-09 03:55:39 +00:00
return os . path . isfile ( get_lnd_channel_backup_file ( ) )
2019-06-15 23:02:44 +00:00
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... "
2021-02-27 17:23:45 +00:00
elif " unable to create server " in line :
return " Network Error "
2020-05-09 03:35:44 +00:00
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
2021-01-16 18:15:52 +00:00
def get_loop_version ( ) :
global loop_version
if loop_version == None :
loop_version = subprocess . check_output ( " loopd --version | egrep -o ' [0-9]+ \\ .[0-9]+ \\ .[0-9]+ ' | head -n 1 " , shell = True )
return " v {} " . format ( loop_version )
def get_pool_version ( ) :
global pool_version
if pool_version == None :
pool_version = subprocess . check_output ( " poold --version | egrep -o ' [0-9]+ \\ .[0-9]+ \\ .[0-9]+ ' | head -n 1 " , shell = True )
return " v {} " . format ( pool_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 "