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)
Ejemplo n.º 3
0
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