def check_for_new_txes(self): logger = self.logger MAX_TX_REQUEST_COUNT = 256 tx_request_count = 2 max_attempts = int(math.log(MAX_TX_REQUEST_COUNT, 2)) for i in range(max_attempts): ##how listtransactions works ##skip and count parameters take most-recent txes first ## so skip=0 count=1 will return the most recent tx ##and skip=0 count=3 will return the 3 most recent txes ##but the actual list returned has the REVERSED order ##skip=0 count=3 will return a list with the most recent tx LAST ret = self.rpc.call("listtransactions", ["*", tx_request_count, 0, True]) ret = ret[::-1] if self.last_known_wallet_txid == None: recent_tx_index = len(ret) #=0 means no new txes break else: txid_list = [(tx["txid"], tx.get("address", None)) for tx in ret] recent_tx_index = next((i for i, (txid, addr) in enumerate(txid_list) if txid == self.last_known_wallet_txid[0] and addr == self.last_known_wallet_txid[1]), -1) if recent_tx_index != -1: break tx_request_count *= 2 #TODO low priority: handle a user getting more than 255 new # transactions in 15 seconds if len(ret) > 0: self.last_known_wallet_txid = (ret[0]["txid"], ret[0].get("address", None)) assert(recent_tx_index != -1) if recent_tx_index == 0: return set() new_txes = ret[:recent_tx_index][::-1] logger.debug("new txes = " + str(new_txes)) obtained_txids = set() updated_scripthashes = [] for tx in new_txes: if "txid" not in tx or "category" not in tx: continue if tx["category"] not in ("receive", "send", "generate", "immature"): continue if tx["confirmations"] < 0: continue #conflicted if tx["txid"] in obtained_txids: continue obtained_txids.add(tx["txid"]) output_scriptpubkeys, input_scriptpubkeys, txd = \ self.get_input_and_output_scriptpubkeys(tx["txid"]) matching_scripthashes = [] for spk in (output_scriptpubkeys + input_scriptpubkeys): scripthash = script_to_scripthash(spk) if scripthash in self.address_history: matching_scripthashes.append(scripthash) if len(matching_scripthashes) == 0: continue for wal in self.deterministic_wallets: overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit( output_scriptpubkeys) if overrun_depths != None: for change, import_count in overrun_depths.items(): new_addrs, spks = wal.get_new_addresses(change, import_count) for spk in spks: self.address_history[script_to_scripthash( spk)] = {'history': [], 'subscribed': False} logger.debug("importing " + str(len(spks)) + " into change=" + str(change)) import_addresses(self.rpc, new_addrs, [], -1, 0, logger) updated_scripthashes.extend(matching_scripthashes) new_history_element = self.generate_new_history_element(tx, txd) logger.info("Found new tx: " + str(new_history_element)) for scrhash in matching_scripthashes: self.address_history[scrhash]["history"].append( new_history_element) if new_history_element["height"] <= 0: self.unconfirmed_txes[tx["txid"]].append(scrhash) if tx["confirmations"] > 0: self.reorganizable_txes.append((tx["txid"], tx["blockhash"], new_history_element["height"], matching_scripthashes)) return set(updated_scripthashes)
def main(): opts = parse_args() try: config = RawConfigParser() config.read(opts.config_file) config.options("master-public-keys") except NoSectionError: print("ERROR: Non-existant configuration file {}".format( opts.config_file)) return logger = logging.getLogger('ELECTRUMPERSONALSERVER') logger, logfilename = logger_config(logger, config) logger.info('Starting Electrum Personal Server ' + str(SERVER_VERSION_NUMBER)) logger.info('Logging to ' + logfilename) logger.debug("Process ID (PID) = " + str(os.getpid())) rpc_u = None rpc_p = None cookie_path = None try: rpc_u = config.get("bitcoin-rpc", "rpc_user") rpc_p = config.get("bitcoin-rpc", "rpc_password") logger.debug("obtaining auth from rpc_user/pass") except NoOptionError: cookie_path = obtain_cookie_file_path( config.get("bitcoin-rpc", "datadir")) logger.debug("obtaining auth from .cookie") if rpc_u == None and cookie_path == None: return rpc = JsonRpc(host=config.get("bitcoin-rpc", "host"), port=int(config.get("bitcoin-rpc", "port")), user=rpc_u, password=rpc_p, cookie_path=cookie_path, wallet_filename=config.get("bitcoin-rpc", "wallet_filename").strip(), logger=logger) #TODO somewhere here loop until rpc works and fully sync'd, to allow # people to run this script without waiting for their node to fully # catch up sync'd when getblockchaininfo blocks == headers, or use # verificationprogress printed_error_msg = False while bestblockhash[0] == None: try: bestblockhash[0] = rpc.call("getbestblockhash", []) except JsonRpcError as e: if not printed_error_msg: logger.error("Error with bitcoin json-rpc: " + repr(e)) printed_error_msg = True time.sleep(5) try: rpc.call("listunspent", []) except JsonRpcError as e: logger.error(repr(e)) logger.error("Wallet related RPC call failed, possibly the " + "bitcoin node was compiled with the disable wallet flag") return test_keydata = ( "2 tpubD6NzVbkrYhZ4YVMVzC7wZeRfz3bhqcHvV8M3UiULCfzFtLtp5nwvi6LnBQegrkx" + "YGPkSzXUEvcPEHcKdda8W1YShVBkhFBGkLxjSQ1Nx3cJ tpubD6NzVbkrYhZ4WjgNYq2nF" + "TbiSLW2SZAzs4g5JHLqwQ3AmR3tCWpqsZJJEoZuP5HAEBNxgYQhtWMezszoaeTCg6FWGQB" + "T74sszGaxaf64o5s") chain = rpc.call("getblockchaininfo", [])["chain"] try: gaplimit = 5 deterministicwallet.parse_electrum_master_public_key( test_keydata, gaplimit, rpc, chain) except ValueError as e: logger.error(repr(e)) logger.error( "Descriptor related RPC call failed. Bitcoin Core 0.20.0" + " or higher required. Exiting..") return if opts.rescan: rescan_script(logger, rpc, opts.rescan_date) return while True: logger.debug("Checking whether rescan is in progress") walletinfo = rpc.call("getwalletinfo", []) if "scanning" in walletinfo and walletinfo["scanning"]: logger.debug("Waiting for Core wallet rescan to finish") time.sleep(300) continue break import_needed, relevant_spks_addrs, deterministic_wallets = \ get_scriptpubkeys_to_monitor(rpc, config) if import_needed: if not relevant_spks_addrs and not deterministic_wallets: #import = true and no addresses means exit return deterministicwallet.import_addresses( rpc, relevant_spks_addrs, deterministic_wallets, change_param=-1, count=int(config.get("bitcoin-rpc", "initial_import_count"))) logger.info( "Done.\nIf recovering a wallet which already has existing" + " transactions, then\nrun the rescan script. If you're confident" + " that the wallets are new\nand empty then there's no need to" + " rescan, just restart this script") else: txmonitor = transactionmonitor.TransactionMonitor( rpc, deterministic_wallets, logger) if not txmonitor.build_address_history(relevant_spks_addrs): return try: run_electrum_server(rpc, txmonitor, config) except KeyboardInterrupt: logger.info('Received KeyboardInterrupt, quitting')
def main(): opts = parse_args() try: config = RawConfigParser() config.read(opts.config_file) config.options("master-public-keys") except NoSectionError: print("ERROR: Non-existant configuration file {}".format( opts.config_file)) return 1 logger = logging.getLogger('ELECTRUMPERSONALSERVER') logger, logfilename = logger_config(logger, config) logger.info('Starting Electrum Personal Server ' + str(SERVER_VERSION_NUMBER)) logger.info('Logging to ' + logfilename) logger.debug("Process ID (PID) = " + str(os.getpid())) rpc_u = None rpc_p = None cookie_path = None try: rpc_u = config.get("groestlcoin-rpc", "rpc_user") rpc_p = config.get("groestlcoin-rpc", "rpc_password") logger.debug("obtaining auth from rpc_user/pass") except NoOptionError: cookie_path = obtain_cookie_file_path( config.get("groestlcoin-rpc", "datadir")) logger.debug("obtaining auth from .cookie") if rpc_u == None and cookie_path == None: return 1 rpc = JsonRpc(host=config.get("groestlcoin-rpc", "host"), port=int(config.get("groestlcoin-rpc", "port")), user=rpc_u, password=rpc_p, cookie_path=cookie_path, wallet_filename=config.get("groestlcoin-rpc", "wallet_filename").strip(), logger=logger) #TODO somewhere here loop until rpc works and fully sync'd, to allow # people to run this script without waiting for their node to fully # catch up sync'd when getblockchaininfo blocks == headers, or use # verificationprogress printed_error_msg = False while bestblockhash[0] == None: try: bestblockhash[0] = rpc.call("getbestblockhash", []) except JsonRpcError as e: if not printed_error_msg: logger.error("Error with groestlcoin json-rpc: " + repr(e)) printed_error_msg = True time.sleep(5) try: rpc.call("listunspent", []) except JsonRpcError as e: logger.error(repr(e)) logger.error( "Wallet related RPC call failed, possibly the " + "groestlcoin node was compiled with the disable wallet flag") return 1 test_keydata = ( "2 tpubD6NzVbkrYhZ4Xh9Ybbw6cna2UyGzYXmpk6bYuvWAUsD493Eb57ssPKC74CoVyQi" + "thN9hRUKZz5c5BQqnS8zqDisJvDTA6fJgyVjhGFtSZaN tpubD6NzVbkrYhZ4XG7HrP7yo" + "9We8rEmc7RTbViHgKwYNwNN71gvuMpYDWRHCUoGMYKZvDMbF3VpWngHvbpTan9Wd3WTk9q" + "ZzZyuYKAGoPJRGjs") chain = rpc.call("getblockchaininfo", [])["chain"] try: gaplimit = 5 deterministicwallet.parse_electrum_master_public_key( test_keydata, gaplimit, rpc, chain) except ValueError as e: logger.error(repr(e)) logger.error( "Descriptor related RPC call failed. Groestlcoin Core 2.20.1" + " or higher required. Exiting..") return 1 if opts.rescan: rescan_script(logger, rpc, opts.rescan_date) return 0 while True: logger.debug("Checking whether rescan is in progress") walletinfo = rpc.call("getwalletinfo", []) if "scanning" in walletinfo and walletinfo["scanning"]: logger.debug("Waiting for Core wallet rescan to finish") time.sleep(300) continue break import_needed, relevant_spks_addrs, deterministic_wallets = \ get_scriptpubkeys_to_monitor(rpc, config) if import_needed: if not relevant_spks_addrs and not deterministic_wallets: #import = true and no addresses means exit return 0 deterministicwallet.import_addresses( rpc, relevant_spks_addrs, deterministic_wallets, change_param=-1, count=int(config.get("groestlcoin-rpc", "initial_import_count"))) logger.info( "Done.\nIf recovering a wallet which already has existing" + " transactions, then\nrun the rescan script. If you're confident" + " that the wallets are new\nand empty then there's no need to" + " rescan, just restart this script") else: txmonitor = transactionmonitor.TransactionMonitor( rpc, deterministic_wallets, logger) if not txmonitor.build_address_history(relevant_spks_addrs): return 1 try: run_electrum_server(rpc, txmonitor, config) except KeyboardInterrupt: logger.info('Received KeyboardInterrupt, quitting') return 1 return 0