#!/usr/bin/env python3 import os import sys from pathlib import Path import grpc if sys.version_info < (3, 0): print("Can't run on Python2") sys.exit() # display config script info if len(sys.argv) <= 1 or sys.argv[1] in ["-h", "--help", "help"]: print("# creating or recovering the LND wallet") print("# lnd.initwallet.py new [mainnet|testnet|signet] [walletpassword] [?seedpassword]") print("# lnd.initwallet.py seed [mainnet|testnet|signet] [walletpassword] [\"seeds-words-seperated-spaces\"] [?seedpassword]") print("# lnd.initwallet.py unlock [mainnet|testnet|signet] [walletpassword] [recovery_window]") print("# lnd.initwallet.py scb [mainnet|testnet|signet] [filepathSCB] [macaroonPath]") print("# lnd.initwallet.py change-password [mainnet|testnet|signet] [walletpassword-old] [walletpassword-new]") print("err='missing parameters'") sys.exit(1) mode = sys.argv[1] if mode == "scb": import codecs from lndlibs import lightning_pb2 as lnrpc from lndlibs import lightning_pb2_grpc as lightningstub else: from lndlibs import walletunlocker_pb2 as lnrpc from lndlibs import walletunlocker_pb2_grpc as rpcstub def new(stub, wallet_password="", seed_entropy=None): if seed_entropy: # provide 16-bytes of static data to get reproducible seeds for TESTING!) print("WARNING: Use this for testing only!!") request = lnrpc.GenSeedRequest(seed_entropy=seed_entropy) else: request = lnrpc.GenSeedRequest() try: response = stub.GenSeed(request) seed_words = response.cipher_seed_mnemonic seed_words_str = ', '.join(seed_words) print("seedwords='" + seed_words_str + "'") # add a 6x4 formatted version to the output seed_words_6x4 = "" for i in range(0, len(seed_words)): if i % 6 == 0 and i != 0: seed_words_6x4 = seed_words_6x4 + "\n" single_word = str(i + 1) + ":" + seed_words[i] while len(single_word) < 12: single_word = single_word + " " seed_words_6x4 = seed_words_6x4 + single_word print("seedwords6x4='" + seed_words_6x4 + "'") except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError GenSeedRequest'") print("errMore=\"" + details + "\"") sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='GenSeedRequest'") sys.exit(1) request = lnrpc.InitWalletRequest( wallet_password=wallet_password.encode(), cipher_seed_mnemonic=seed_words ) try: response = stub.InitWallet(request) except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError InitWallet'") print("errMore=\"" + details + "\"") sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='InitWallet'") sys.exit(1) # init wallet with recovery_window=0 (to not trigger rescan) # but make sure to put lnd into recovery mode with lnd.backup.sh # script after that def seed(stub, wallet_password="", seed_words="", seed_password=""): request = lnrpc.InitWalletRequest( wallet_password=wallet_password.encode(), cipher_seed_mnemonic=[x.encode() for x in seed_words], recovery_window=0, aezeed_passphrase=seed_password.encode() ) try: response = stub.InitWallet(request) except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError InitWallet'") print("errMore=\"" + details + "\"") sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='InitWallet'") sys.exit(1) def unlock(stub, wallet_password="", scan_depth=int): request = lnrpc.UnlockWalletRequest( wallet_password=wallet_password.encode(), recovery_window=scan_depth, ) try: response = stub.UnlockWallet(request) print("# ok - wallet unlocked - using recovery window:", scan_depth) except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError UnlockWallet'") print("errMore=\"" + details + "\"") sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='UnlockWallet'") sys.exit(1) def scb(stub, file_path_scb="", macaroon_path=""): macaroon_file = Path(macaroon_path) print(macaroon_file) macaroon = codecs.encode(open(macaroon_file, 'rb').read(), 'hex') with open(file_path_scb, 'rb') as f: content = f.read() request = lnrpc.RestoreChanBackupRequest( multi_chan_backup=content ) try: response = stub.RestoreChannelBackups(request, metadata=[('macaroon', macaroon)]) print(response) except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError RestoreChanBackupRequest'") print("errMore=\"" + details + "\"") sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='RestoreChanBackupRequest'") sys.exit(1) def change_password(stub, wallet_password="", wallet_password_new=""): request = lnrpc.ChangePasswordRequest( current_password=wallet_password.encode(), new_password=wallet_password_new.encode() ) try: response = stub.ChangePassword(request) print("# ok - password changed") #print(response.admin_macaroon) except grpc.RpcError as rpc_error_call: code = rpc_error_call.code() print(code, file=sys.stderr) details = rpc_error_call.details() print("err='RPCError ChangePassword'") print("errMore=\"" + details + "\"") print("# make sure wallet is locked when trying to change password'", file=sys.stderr) sys.exit(1) except: e = sys.exc_info()[0] print(e, file=sys.stderr) print("err='ChangePassword'") sys.exit(1) def parse_args(): network = "" wallet_password = "" wallet_password_new = "" seed_words = "" seed_password = "" filepath_scb = "" macaroon_path = "" scan_depth = int if len(sys.argv) > 2: network = sys.argv[2] else: print("err='missing parameters'") sys.exit(1) if mode == "new": if len(sys.argv) > 3: wallet_password = sys.argv[3] if len(wallet_password) < 8: print("err='wallet password is too short'") sys.exit(1) else: print("err='missing parameters'") sys.exit(1) if len(sys.argv) > 4: seed_password = sys.argv[4] elif mode == "change-password": if len(sys.argv) > 4: wallet_password = sys.argv[3] if len(wallet_password) < 8: print("err='wallet password is too short'") sys.exit(1) wallet_password_new = sys.argv[4] if len(wallet_password_new ) < 8: print("err='wallet password new is too short'") sys.exit(1) else: print("err='missing parameters'") sys.exit(1) elif mode == "seed": if len(sys.argv) > 3: wallet_password = sys.argv[3] if len(wallet_password) < 8: print("err='wallet password is too short'") sys.exit(1) else: print("err='not correct amount of parameter - missing wallet password'") sys.exit(1) if len(sys.argv) > 4: seed_word_str = sys.argv[4] seed_words = seed_word_str.split(" ") if len(seed_words) < 24: print("err='not 24 seed words separated by just spaces (surrounded with \")'") sys.exit(1) else: print("err='not correct amount of parameter - missing seed string'") sys.exit(1) if len(sys.argv) > 5: seed_password = sys.argv[5] elif mode == "unlock": if len(sys.argv) > 3: wallet_password = sys.argv[3] if len(wallet_password) < 8: print("err='wallet password is too short'") sys.exit(1) else: print("err='not correct amount of parameter - missing wallet password'") sys.exit(1) if len(sys.argv) > 4: scan_depth = int(sys.argv[4]) if type(scan_depth) is not int: print("err='expecting a number for recovery_window'") sys.exit(1) else: print("err='not correct amount of parameter - missing recovery_window'") sys.exit(1) elif mode == "scb": if len(sys.argv) > 3: filepath_scb = sys.argv[3] scb_file = Path(filepath_scb) if scb_file.is_file(): print("# OK the SCB file exists") else: print("err='the given filepathSCB - file does not exist or no permission'") sys.exit(1) else: print("err='not correct amount of parameter - missing filepathSCB'") sys.exit(1) if len(sys.argv) > 4: macaroon_path = sys.argv[4] macaroon_file = Path(macaroon_path) if macaroon_file.is_file(): print("# OK the admin.macaroon exists") else: print("err='the given macaroonPath - file does not exist or no permission'") sys.exit(1) else: print("err='not correct amount of parameter - missing macaroonPath'") sys.exit(1) else: print("err='unknown mode parameter - run without any parameters to see options'") sys.exit(1) return network, wallet_password, seed_words, seed_password, filepath_scb, macaroon_path, wallet_password_new, scan_depth def main(): network, wallet_password, seed_words, seed_password, file_path_scb, macaroon_path, wallet_password_new, scan_depth = parse_args() grpcEndpoint="localhost:0" if network == "mainnet": grpcEndpoint="localhost:10009" elif network == "testnet": grpcEndpoint="localhost:11009" elif network == "signet": grpcEndpoint="localhost:13009" else: print("err='chain not supported'") sys.exit(1) os.environ['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA' cert = open('/mnt/hdd/lnd/tls.cert', 'rb').read() ssl_creds = grpc.ssl_channel_credentials(cert) channel = grpc.secure_channel(grpcEndpoint, ssl_creds) if mode == "scb": stub = lightningstub.LightningStub(channel) else: stub = rpcstub.WalletUnlockerStub(channel) if mode == "new": print("# *** CREATING NEW LND WALLET ***") new(stub, wallet_password) elif mode == "seed": print("# *** RECOVERING LND WALLET FROM SEED ***") seed(stub, wallet_password, seed_words, seed_password) elif mode == "unlock": print("# *** UNLOCK WALLET WITH PASSWORD_C ***") unlock(stub, wallet_password, scan_depth) elif mode == "scb": print("# *** RECOVERING LND CHANNEL FUNDS FROM SCB ***") scb(stub, file_path_scb, macaroon_path) elif mode == "change-password": print("# *** SETTING NEW PASSWORD FOR WALLET ***") change_password(stub, wallet_password, wallet_password_new) if __name__ == '__main__': main()