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
def get_scriptpubkeys_to_monitor(rpc, config):
    logger = logging.getLogger('ELECTRUMPERSONALSERVER')
    st = time.time()
    try:
        imported_addresses = set(
            rpc.call("getaddressesbyaccount",
                     [deterministicwallet.ADDRESSES_LABEL]))
        logger.debug("using deprecated accounts interface")
    except JsonRpcError:
        #bitcoin core 0.17 deprecates accounts, replaced with labels
        if deterministicwallet.ADDRESSES_LABEL in rpc.call("listlabels", []):
            imported_addresses = set(
                rpc.call("getaddressesbylabel",
                         [deterministicwallet.ADDRESSES_LABEL]).keys())
        else:
            #no label, no addresses imported at all
            imported_addresses = set()

    deterministic_wallets = []
    for key in config.options("master-public-keys"):
        mpk = config.get("master-public-keys", key)
        gaplimit = int(config.get("bitcoin-rpc", "gap_limit"))
        chain = rpc.call("getblockchaininfo", [])["chain"]
        try:
            wal = deterministicwallet.parse_electrum_master_public_key(
                mpk, gaplimit, rpc, chain)
        except ValueError:
            raise ValueError("Bad master public key format. Get it from " +
                             "Electrum menu `Wallet` -> `Information`")
        deterministic_wallets.append(wal)

    #check whether these deterministic wallets have already been imported
    import_needed = False
    wallets_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_addrs, first_spk = wal.get_addresses(change=0,
                                                   from_index=0,
                                                   count=TEST_ADDR_COUNT)
        logger.info("\n" + config_mpk_key + " =>\n\t" +
                    "\n\t".join(first_addrs))
        last_addr, last_spk = wal.get_addresses(
            change=0,
            from_index=int(config.get("bitcoin-rpc", "initial_import_count")) -
            1,
            count=1)
        if not set(first_addrs + last_addr).issubset(imported_addresses):
            import_needed = True
            wallets_to_import.append(wal)
    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 none other params means exit
        return (True, None, None)

    #if addresses need to be imported then return them
    if import_needed:
        #TODO minus imported_addresses
        logger.info("Importing " + str(len(wallets_to_import)) +
                    " wallets and " +
                    str(len(watch_only_addresses_to_import)) +
                    " watch-only addresses into the Bitcoin node")
        time.sleep(5)
        return True, watch_only_addresses_to_import, wallets_to_import

    #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]:
            addrs, spks = wal.get_addresses(
                change, 0,
                int(config.get("bitcoin-rpc", "initial_import_count")))
            spks_to_monitor.extend(spks)
            #loop until one address found that isnt imported
            while True:
                addrs, spks = wal.get_new_addresses(change, count=1)
                if addrs[0] not in imported_addresses:
                    break
                spks_to_monitor.append(spks[0])
            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
Beispiel #4
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))
        if not set(first_addrs).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 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