#!/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()