def _derive_addresses(self, change, from_index, count): result = [] for index in range(from_index, from_index + count): pubkey = btc.electrum_pubkey(self.mpk, index, change) scriptpubkey = self._pubkey_to_scriptpubkey(pubkey) result.append(script_to_address(scriptpubkey, self.rpc)) return result
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): logger.debug("listtransactions tx_request_count=" + str(tx_request_count)) ##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 logger.debug("recent tx index = " + str(recent_tx_index) + " ret = " + str([(t["txid"], t.get("address", None)) for t in ret])) if len(ret) > 0: self.last_known_wallet_txid = (ret[0]["txid"], ret[0].get("address", None)) logger.debug("last_known_wallet_txid = " + str(self.last_known_wallet_txid)) 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(): spks = wal.get_new_scriptpubkeys(change, import_count) for spk in spks: self.address_history[script_to_scripthash(spk)] = { 'history': [], 'subscribed': False } new_addrs = [ script_to_address(s, self.rpc) for s in spks ] logger.debug("importing " + str(len(spks)) + " into change=" + str(change)) import_addresses(self.rpc, new_addrs, 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 get_scriptpubkeys_to_monitor(rpc, config): logger = logging.getLogger('ELECTRUMPERSONALSERVER') st = time.time() try: imported_addresses = set(rpc.call("getaddressesbyaccount", [transactionmonitor.ADDRESSES_LABEL])) logger.debug("using deprecated accounts interface") except JsonRpcError: #bitcoin core 0.17 deprecates accounts, replaced with labels if transactionmonitor.ADDRESSES_LABEL in rpc.call("listlabels", []): imported_addresses = set(rpc.call("getaddressesbylabel", [transactionmonitor.ADDRESSES_LABEL]).keys()) else: #no label, no addresses imported at all imported_addresses = set() logger.debug("already-imported addresses = " + str(imported_addresses)) deterministic_wallets = [] for key in config.options("master-public-keys"): wal = deterministicwallet.parse_electrum_master_public_key( config.get("master-public-keys", key), int(config.get("bitcoin-rpc", "gap_limit"))) deterministic_wallets.append(wal) #check whether these deterministic wallets have already been imported import_needed = False wallets_imported = 0 spks_to_import = [] TEST_ADDR_COUNT = 3 logger.info("Displaying first " + str(TEST_ADDR_COUNT) + " addresses of " + "each master public key:") for config_mpk_key, wal in zip(config.options("master-public-keys"), deterministic_wallets): first_spks = wal.get_scriptpubkeys(change=0, from_index=0, count=TEST_ADDR_COUNT) first_addrs = [hashes.script_to_address(s, rpc) for s in first_spks] logger.info("\n" + config_mpk_key + " =>\n\t" + "\n\t".join( first_addrs)) last_spk = wal.get_scriptpubkeys(0, int(config.get("bitcoin-rpc", "initial_import_count")) - 1, 1) last_addr = [hashes.script_to_address(last_spk[0], rpc)] if not set(first_addrs + last_addr).issubset(imported_addresses): import_needed = True wallets_imported += 1 for change in [0, 1]: spks_to_import.extend(wal.get_scriptpubkeys(change, 0, int(config.get("bitcoin-rpc", "initial_import_count")))) logger.info("Obtaining bitcoin addresses to monitor . . .") #check whether watch-only addresses have been imported watch_only_addresses = [] for key in config.options("watch-only-addresses"): watch_only_addresses.extend(config.get("watch-only-addresses", key).split(' ')) watch_only_addresses = set(watch_only_addresses) watch_only_addresses_to_import = [] if not watch_only_addresses.issubset(imported_addresses): import_needed = True watch_only_addresses_to_import = (watch_only_addresses - imported_addresses) if len(deterministic_wallets) == 0 and len(watch_only_addresses) == 0: logger.error("No master public keys or watch-only addresses have " + "been configured at all. Exiting..") #import = true and no addresses means exit return (True, [], None) #if addresses need to be imported then return them if import_needed: addresses_to_import = [hashes.script_to_address(spk, rpc) for spk in spks_to_import] #TODO minus imported_addresses logger.info("Importing " + str(wallets_imported) + " wallets and " + str(len(watch_only_addresses_to_import)) + " watch-only " + "addresses into the Bitcoin node") time.sleep(5) return (True, addresses_to_import + list( watch_only_addresses_to_import), None) #test # importing one det wallet and no addrs, two det wallets and no addrs # no det wallets and some addrs, some det wallets and some addrs #at this point we know we dont need to import any addresses #find which index the deterministic wallets are up to spks_to_monitor = [] for wal in deterministic_wallets: for change in [0, 1]: spks_to_monitor.extend(wal.get_scriptpubkeys(change, 0, int(config.get("bitcoin-rpc", "initial_import_count")))) #loop until one address found that isnt imported while True: spk = wal.get_new_scriptpubkeys(change, count=1)[0] spks_to_monitor.append(spk) if hashes.script_to_address(spk, rpc) not in imported_addresses: break spks_to_monitor.pop() wal.rewind_one(change) spks_to_monitor.extend([hashes.address_to_script(addr, rpc) for addr in watch_only_addresses]) et = time.time() logger.info("Obtained list of addresses to monitor in " + str(et - st) + "sec") return False, spks_to_monitor, deterministic_wallets