def test_device_wallets(
    bitcoin_regtest, devices_filled_data_folder, device_manager, caplog
):
    caplog.set_level(logging.DEBUG)
    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoin_regtest.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    device = device_manager.get_by_alias("trezor")
    assert len(device.wallets(wm)) == 0
    wallet = wm.create_wallet("a_test_wallet", 1, "wpkh", [device.keys[5]], [device])
    assert len(device.wallets(wm)) == 1
    assert device.wallets(wm)[0].alias == wallet.alias
    second_device = device_manager.get_by_alias("specter")
    multisig_wallet = wm.create_wallet(
        "a_multisig_test_wallet",
        1,
        "wsh",
        [device.keys[7], second_device.keys[0]],
        [device, second_device],
    )

    assert len(device.wallets(wm)) == 2
    assert device.wallets(wm)[0].alias == wallet.alias
    assert device.wallets(wm)[1].alias == multisig_wallet.alias

    assert len(second_device.wallets(wm)) == 1
    assert second_device.wallets(wm)[0].alias == multisig_wallet.alias
def test_wallet_change_addresses(
    bitcoin_regtest, devices_filled_data_folder, device_manager
):
    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoin_regtest.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    # A wallet-creation needs a device
    device = device_manager.get_by_alias("specter")
    key = Key.from_json(
        {
            "derivation": "m/48h/1h/0h/2h",
            "original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
            "fingerprint": "08686ac6",
            "type": "wsh",
            "xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL",
        }
    )
    wallet = wm.create_wallet("a_second_test_wallet", 1, "wpkh", [key], [device])

    address = wallet.address
    change_address = wallet.change_address
    assert wallet.addresses == [address]
    assert wallet.change_addresses == [change_address]

    wallet.rpc.generatetoaddress(20, change_address)
    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(110, random_address)
    wallet.getdata()
def test_wallet_labeling(bitcoin_regtest, devices_filled_data_folder, device_manager):
    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoin_regtest.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    # A wallet-creation needs a device
    device = device_manager.get_by_alias("specter")
    key = Key.from_json(
        {
            "derivation": "m/48h/1h/0h/2h",
            "original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
            "fingerprint": "08686ac6",
            "type": "wsh",
            "xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL",
        }
    )
    wallet = wm.create_wallet("a_second_test_wallet", 1, "wpkh", [key], [device])

    address = wallet.address
    assert wallet.getlabel(address) == "Address #0"
    wallet.setlabel(address, "Random label")
    assert wallet.getlabel(address) == "Random label"

    wallet.rpc.generatetoaddress(20, address)

    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(100, random_address)

    # update utxo
    wallet.getdata()
    # update balance
    wallet.get_balance()

    address_balance = wallet.fullbalance
    assert len(wallet.full_utxo) == 20

    new_address = wallet.getnewaddress()
    wallet.setlabel(new_address, "")
    wallet.rpc.generatetoaddress(20, new_address)

    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(100, random_address)

    wallet.getdata()
    wallet.get_balance()

    assert len(wallet.full_utxo) == 40

    wallet.setlabel(new_address, "")
    third_address = wallet.getnewaddress()

    wallet.getdata()
    assert sorted(wallet.addresses) == sorted([address, new_address, third_address])
Esempio n. 4
0
def test_wallet_sortedmulti(bitcoin_regtest, devices_filled_data_folder,
                            device_manager):
    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoin_regtest.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    device = device_manager.get_by_alias("trezor")
    second_device = device_manager.get_by_alias("specter")
    for i in range(2):
        if i == 0:
            multisig_wallet = wm.create_wallet(
                "a_multisig_test_wallet",
                1,
                "wsh",
                [device.keys[7], second_device.keys[0]],
                [device, second_device],
            )
        else:
            multisig_wallet = wm.create_wallet(
                "a_multisig_test_wallet",
                1,
                "wsh",
                [second_device.keys[0], device.keys[7]],
                [second_device, device],
            )

        address = multisig_wallet.address
        address_info = multisig_wallet.rpc.getaddressinfo(address)
        assert address_info["pubkeys"][0] < address_info["pubkeys"][1]

        another_address = multisig_wallet.getnewaddress()
        another_address_info = multisig_wallet.rpc.getaddressinfo(
            another_address)
        assert another_address_info["pubkeys"][0] < another_address_info[
            "pubkeys"][1]

        third_address = multisig_wallet.get_address(30)
        third_address_info = multisig_wallet.rpc.getaddressinfo(third_address)
        assert third_address_info["pubkeys"][0] < third_address_info[
            "pubkeys"][1]

        change_address = multisig_wallet.change_address
        change_address_info = multisig_wallet.rpc.getaddressinfo(
            change_address)
        assert change_address_info["pubkeys"][0] < change_address_info[
            "pubkeys"][1]

        another_change_address = multisig_wallet.get_address(30, change=True)
        another_change_address_info = multisig_wallet.rpc.getaddressinfo(
            another_change_address)
        assert (another_change_address_info["pubkeys"][0] <
                another_change_address_info["pubkeys"][1])
def test_wallet_createpsbt(docker, request, devices_filled_data_folder, device_manager):
    # Instantiate a fresh bitcoind instance to isolate this test.
    bitcoind_controller = instantiate_bitcoind_controller(
        docker, request, rpcport=18998
    )

    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoind_controller.rpcconn.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    # A wallet-creation needs a device
    device = device_manager.get_by_alias("specter")
    key = Key.from_json(
        {
            "derivation": "m/48h/1h/0h/2h",
            "original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
            "fingerprint": "08686ac6",
            "type": "wsh",
            "xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL",
        }
    )
    wallet = wm.create_wallet("a_second_test_wallet", 1, "wpkh", [key], [device])
    # Let's fund the wallet with ... let's say 40 blocks a 50 coins each --> 200 coins
    address = wallet.getnewaddress()
    assert address == "bcrt1qtnrv2jpygx2ef3zqfjhqplnycxak2m6ljnhq6z"
    wallet.rpc.generatetoaddress(20, address)
    # in two addresses
    address = wallet.getnewaddress()
    wallet.rpc.generatetoaddress(20, address)
    # newly minted coins need 100 blocks to get spendable
    # let's mine another 100 blocks to get these coins spendable
    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(110, random_address)
    # update the wallet data
    wallet.get_balance()
    # Now we have loads of potential inputs
    # Let's spend 500 coins
    assert wallet.fullbalance >= 250
    # From this print-statement, let's grab some txids which we'll use for coinselect
    unspents = wallet.rpc.listunspent(0)
    # Lets take 3 more or less random txs from the unspents:
    selected_coins = [
        "{},{}".format(unspents[5]["txid"], unspents[5]["vout"]),
        "{},{}".format(unspents[9]["txid"], unspents[9]["vout"]),
        "{},{}".format(unspents[12]["txid"], unspents[12]["vout"]),
    ]
    selected_coins_amount_sum = (
        unspents[5]["amount"] + unspents[9]["amount"] + unspents[12]["amount"]
    )
    number_of_coins_to_spend = (
        selected_coins_amount_sum - 0.1
    )  # Let's spend almost all of them
    psbt = wallet.createpsbt(
        [random_address],
        [number_of_coins_to_spend],
        True,
        0,
        10,
        selected_coins=selected_coins,
    )
    assert len(psbt["tx"]["vin"]) == 3
    psbt_txs = [tx["txid"] for tx in psbt["tx"]["vin"]]
    for coin in selected_coins:
        assert coin.split(",")[0] in psbt_txs

    # Now let's spend more coins than we have selected. This should result in an exception:
    try:
        psbt = wallet.createpsbt(
            [random_address],
            [number_of_coins_to_spend + 1],
            True,
            0,
            10,
            selected_coins=selected_coins,
        )
        assert False, "should throw an exception!"
    except SpecterError as e:
        pass

    assert wallet.locked_amount == selected_coins_amount_sum
    assert len(wallet.rpc.listlockunspent()) == 3
    assert (
        wallet.full_available_balance == wallet.fullbalance - selected_coins_amount_sum
    )

    wallet.delete_pending_psbt(psbt["tx"]["txid"])
    assert wallet.locked_amount == 0
    assert len(wallet.rpc.listlockunspent()) == 0
    assert wallet.full_available_balance == wallet.fullbalance

    # cleanup
    bitcoind_controller.stop_bitcoind()
def test_WalletManager(docker, request, devices_filled_data_folder, device_manager):
    # Instantiate a fresh bitcoind instance to isolate this test.
    bitcoind_controller = instantiate_bitcoind_controller(
        docker, request, rpcport=18998
    )

    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoind_controller.rpcconn.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    # A wallet-creation needs a device
    device = device_manager.get_by_alias("trezor")
    assert device != None
    # Lets's create a wallet with the WalletManager
    wm.create_wallet("a_test_wallet", 1, "wpkh", [device.keys[5]], [device])
    # The wallet-name gets its filename and therefore its alias
    wallet = wm.wallets["a_test_wallet"]
    assert wallet != None
    assert wallet.balance["trusted"] == 0
    assert wallet.balance["untrusted_pending"] == 0
    # this is a sum of both
    assert wallet.fullbalance == 0
    address = wallet.getnewaddress()
    # newly minted coins need 100 blocks to get spendable
    wallet.rpc.generatetoaddress(1, address)
    # let's mine another 100 blocks to get these coins spendable
    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(100, random_address)
    # update the balance
    wallet.get_balance()
    assert wallet.fullbalance >= 25

    # You can create a multisig wallet with the wallet manager like this
    second_device = device_manager.get_by_alias("specter")
    multisig_wallet = wm.create_wallet(
        "a_multisig_test_wallet",
        1,
        "wsh",
        [device.keys[7], second_device.keys[0]],
        [device, second_device],
    )

    assert len(wm.wallets) == 2
    assert multisig_wallet != None
    assert multisig_wallet.fullbalance == 0
    multisig_address = multisig_wallet.getnewaddress()
    multisig_wallet.rpc.generatetoaddress(1, multisig_address)
    multisig_wallet.rpc.generatetoaddress(100, random_address)
    # update balance
    multisig_wallet.get_balance()
    assert multisig_wallet.fullbalance >= 12.5
    # The WalletManager also has a `wallets_names` property, returning a sorted list of the names of all wallets
    assert wm.wallets_names == ["a_multisig_test_wallet", "a_test_wallet"]

    # You can rename a wallet using the wallet manager using `rename_wallet`, passing the wallet object and the new name to assign to it
    wm.rename_wallet(multisig_wallet, "new_name_test_wallet")
    assert multisig_wallet.name == "new_name_test_wallet"
    assert wm.wallets_names == ["a_test_wallet", "new_name_test_wallet"]

    # you can also delete a wallet by passing it to the wallet manager's `delete_wallet` method
    # it will delete the json and attempt to remove it from Bitcoin Core
    wallet_fullpath = multisig_wallet.fullpath
    assert os.path.exists(wallet_fullpath)
    wm.delete_wallet(multisig_wallet)
    assert not os.path.exists(wallet_fullpath)
    assert len(wm.wallets) == 1

    # cleanup
    bitcoind_controller.stop_bitcoind()
Esempio n. 7
0
def test_abandon_purged_tx(caplog, docker, request, devices_filled_data_folder,
                           device_manager):
    # Specter should support calling abandontransaction if a pending tx has been purged
    # from the mempool. Test starts a new bitcoind with a restricted mempool to make it
    # easier to spam the mempool and purge our target tx.
    # TODO: Similar test but for maxmempoolexpiry?

    # Copied and adapted from:
    #    https://github.com/bitcoin/bitcoin/blob/master/test/functional/mempool_limit.py
    from bitcoin_core.test.functional.test_framework.util import (
        gen_return_txouts,
        satoshi_round,
        create_lots_of_big_transactions,
    )
    from conftest import instantiate_bitcoind_controller

    caplog.set_level(logging.DEBUG)

    # ==== Specter-specific: do custom setup ====
    # Instantiate a new bitcoind w/limited mempool. Use a different port to not interfere
    # with existing instance for other tests.
    bitcoind_controller = instantiate_bitcoind_controller(
        docker,
        request,
        rpcport=18998,
        extra_args=[
            "-acceptnonstdtxn=1", "-maxmempool=5", "-spendzeroconfchange=0"
        ],
    )
    try:
        assert bitcoind_controller.get_rpc().test_connection()
        rpcconn = bitcoind_controller.rpcconn
        rpc = rpcconn.get_rpc()
        assert rpc is not None
        assert rpc.ipaddress != None

        # Note: Our utxo creation is simpler than mempool_limit.py's approach since we're
        # running in regtest and can just use generatetoaddress().

        # Instantiate a new Specter instance to talk to this bitcoind
        config = {
            "rpc": {
                "autodetect": False,
                "datadir": "",
                "user": rpcconn.rpcuser,
                "password": rpcconn.rpcpassword,
                "port": rpcconn.rpcport,
                "host": rpcconn.ipaddress,
                "protocol": "http",
            },
            "auth": {
                "method": "rpcpasswordaspin",
            },
        }
        specter = Specter(data_folder=devices_filled_data_folder,
                          config=config)
        specter.check()

        assert specter.info["mempool_info"][
            "maxmempool"] == 5 * 1000 * 1000  # 5MB

        # Largely copy-and-paste from test_wallet_manager.test_wallet_createpsbt.
        # TODO: Make a test fixture in conftest.py that sets up already funded wallets
        # for a bitcoin core hot wallet.
        wallet_manager = WalletManager(
            210100,
            devices_filled_data_folder,
            rpc,
            "regtest",
            device_manager,
            allow_threading=False,
        )

        # Create a new device that can sign psbts (Bitcoin Core hot wallet)
        device = device_manager.add_device(name="bitcoin_core_hot_wallet",
                                           device_type="bitcoincore",
                                           keys=[])
        device.setup_device(file_password=None, wallet_manager=wallet_manager)
        device.add_hot_wallet_keys(
            mnemonic=generate_mnemonic(strength=128),
            passphrase="",
            paths=["m/84h/1h/0h", "m/84h/1h/1h"],
            file_password=None,
            wallet_manager=wallet_manager,
            testnet=True,
            keys_range=[0, 1000],
            keys_purposes=[],
        )

        wallet = wallet_manager.create_wallet("bitcoincore_test_wallet", 1,
                                              "wpkh", [device.keys[0]],
                                              [device])
        # dummy wallet that will do the mining
        dummy = wallet_manager.create_wallet("bitcoincore_dummy", 1, "wpkh",
                                             [device.keys[1]], [device])

        # Fund the wallet. Going to need a LOT of utxos to play with.
        logging.info("Generating utxos to wallet")
        address = wallet.getnewaddress()
        wallet.rpc.generatetoaddress(1, address)
        dummy_address = dummy.getnewaddress()
        dummy.rpc.generatetoaddress(90, dummy_address)

        # newly minted coins need 100 blocks to get spendable
        # let's mine another 100 blocks to get these coins spendable
        dummy.rpc.generatetoaddress(101, dummy_address)

        # update the wallet data
        wallet.update_balance()
        dummy.update_balance()

        # ==== Begin test from mempool_limit.py ====
        txouts = gen_return_txouts()
        relayfee = satoshi_round(rpc.getnetworkinfo()["relayfee"])

        logging.info("Check that mempoolminfee is minrelytxfee")
        assert satoshi_round(
            rpc.getmempoolinfo()["minrelaytxfee"]) == Decimal("0.00001000")
        assert satoshi_round(
            rpc.getmempoolinfo()["mempoolminfee"]) == Decimal("0.00001000")

        utxos = wallet.rpc.listunspent()

        logging.info("Create a mempool tx that will be evicted")
        us0 = utxos.pop()
        inputs = [{"txid": us0["txid"], "vout": us0["vout"]}]
        outputs = {wallet.getnewaddress(): 0.0001}
        tx = wallet.rpc.createrawtransaction(inputs, outputs)
        wallet.rpc.settxfee(
            str(relayfee))  # specifically fund this tx with low fee
        txF = wallet.rpc.fundrawtransaction(tx, {"change_type": "bech32"})
        wallet.rpc.settxfee(0)  # return to automatic fee selection
        psbtF = wallet.rpc.converttopsbt(txF["hex"])
        psbtFF = wallet.rpc.walletprocesspsbt(psbtF)
        signed = device.sign_psbt(psbtFF["psbt"], wallet)
        assert signed["complete"]
        finalized = wallet.rpc.finalizepsbt(signed["psbt"])
        txid = wallet.rpc.sendrawtransaction(finalized["hex"])

        # ==== Specter-specific: can't abandon a valid pending tx ====
        try:
            wallet.abandontransaction(txid)
        except SpecterError as e:
            assert "Cannot abandon" in str(e)

        # ==== Resume test from mempool_limit.py ====
        # Spam the mempool with big transactions!
        txids = []
        dummy_utxos = dummy.rpc.listunspent()
        relayfee = satoshi_round(rpc.getnetworkinfo()["relayfee"])
        base_fee = float(relayfee) * 100
        for i in range(3):
            txids.append([])
            txids[i] = create_lots_of_big_transactions(
                dummy, txouts, dummy_utxos[30 * i:30 * i + 30], 30,
                (i + 1) * base_fee)

        logging.info("The tx should be evicted by now")
        assert txid not in wallet.rpc.getrawmempool()
        txdata = wallet.rpc.gettransaction(txid)
        assert txdata["confirmations"] == 0  # confirmation should still be 0

        # ==== Specter-specific: Verify purge and abandon ====
        assert wallet.is_tx_purged(txid)
        wallet.abandontransaction(txid)

        # tx will still be in the wallet but marked "abandoned"
        txdata = wallet.rpc.gettransaction(txid)
        for detail in txdata["details"]:
            if detail["category"] == "send":
                assert detail["abandoned"]

        # at this point we should have all the balance in trusted
        # nothing in untrusted
        untrusted = wallet.update_balance()["untrusted_pending"]
        assert untrusted == 0
        # Can we now spend those same inputs?
        outputs = {wallet.getnewaddress(): 0.0001}
        tx = wallet.rpc.createrawtransaction(inputs, outputs)

        # Fund this tx with a high enough fee
        relayfee = satoshi_round(rpc.getnetworkinfo()["relayfee"])
        wallet.rpc.settxfee(str(relayfee * Decimal("3.0")))

        txF = wallet.rpc.fundrawtransaction(tx)
        wallet.rpc.settxfee(0)  # return to automatic fee selection
        psbtF = wallet.rpc.converttopsbt(txF["hex"])
        psbtFF = wallet.rpc.walletprocesspsbt(psbtF)
        signed = device.sign_psbt(psbtFF["psbt"], wallet)
        assert signed["complete"]
        finalized = wallet.rpc.finalizepsbt(signed["psbt"])
        txid = wallet.rpc.sendrawtransaction(finalized["hex"])

        # Should have been accepted by the mempool
        assert txid in wallet.rpc.getrawmempool()
        # Our balance should go to untrusted now as it's unconfirmed
        assert wallet.update_balance()["untrusted_pending"] > 0
    finally:
        # Clean up
        bitcoind_controller.stop_bitcoind()
def test_import_address_labels(caplog, docker, request,
                               devices_filled_data_folder, device_manager):
    caplog.set_level(logging.DEBUG)

    # ==== Specter-specific: do custom setup ====
    # Instantiate a new bitcoind w/limited mempool. Use a different port to not interfere
    # with existing instance for other tests.
    bitcoind_controller = instantiate_bitcoind_controller(
        docker,
        request,
        rpcport=18968,
        extra_args=[
            "-acceptnonstdtxn=1", "-maxmempool=5", "-spendzeroconfchange=0"
        ],
    )
    try:
        assert bitcoind_controller.get_rpc().test_connection()
        rpcconn = bitcoind_controller.rpcconn
        rpc = rpcconn.get_rpc()
        assert rpc is not None
        assert rpc.ipaddress != None

        # Note: Our utxo creation is simpler than mempool_limit.py's approach since we're
        # running in regtest and can just use generatetoaddress().

        # Instantiate a new Specter instance to talk to this bitcoind
        config = {
            "rpc": {
                "autodetect": False,
                "datadir": "",
                "user": rpcconn.rpcuser,
                "password": rpcconn.rpcpassword,
                "port": rpcconn.rpcport,
                "host": rpcconn.ipaddress,
                "protocol": "http",
            },
            "auth": {
                "method": "rpcpasswordaspin",
            },
        }
        specter = Specter(data_folder=devices_filled_data_folder,
                          config=config)
        specter.check()

        # Largely copy-and-paste from test_wallet_manager.test_wallet_createpsbt.
        # TODO: Make a test fixture in conftest.py that sets up already funded wallets
        # for a bitcoin core hot wallet.
        wallet_manager = WalletManager(
            200100,
            devices_filled_data_folder,
            rpc,
            "regtest",
            device_manager,
            allow_threading=False,
        )

        # Create a new device that can sign psbts (Bitcoin Core hot wallet)
        device = device_manager.add_device(name="bitcoin_core_hot_wallet",
                                           device_type="bitcoincore",
                                           keys=[])
        device.setup_device(file_password=None, wallet_manager=wallet_manager)
        device.add_hot_wallet_keys(
            mnemonic=
            "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
            passphrase="",
            paths=["m/49h/0h/0h"],
            file_password=None,
            wallet_manager=wallet_manager,
            testnet=True,
            keys_range=[0, 1000],
            keys_purposes=[],
        )

        wallet = wallet_manager.create_wallet("bitcoincore_test_wallet", 1,
                                              "sh-wpkh", [device.keys[0]],
                                              [device])

        # Fund the wallet. Going to need a LOT of utxos to play with.
        logger.info("Generating utxos to wallet")
        test_address = wallet.getnewaddress(
        )  # 2NCSZrX49HHyzUy6oj8ggm9WD19hFvjzzou

        wallet.rpc.generatetoaddress(1, test_address)[0]

        # newly minted coins need 100 blocks to get spendable
        # let's mine another 100 blocks to get these coins spendable
        trash_address = wallet.getnewaddress()
        wallet.rpc.generatetoaddress(100, trash_address)

        # the utxo is only available after the 100 mined blocks
        utxos = wallet.rpc.listunspent()
        # txid of the funding of test_address
        txid = utxos[0]["txid"]

        assert wallet._addresses[test_address]["label"] is None
        number_of_addresses = len(wallet._addresses)

        # Electrum
        # Test it with a txid label that does not belong to the wallet -> should be ignored
        wallet.import_address_labels(
            json.dumps({
                "8d0958cb8701fac7421eb077e44b36809b90c7ad4a35e0c607c2cd591c522668":
                "txid label"
            }))
        assert wallet._addresses[test_address]["label"] is None
        assert len(wallet._addresses) == number_of_addresses

        # Test it with an address label that does not belong to the wallet -> should be ignored
        wallet.import_address_labels(
            json.dumps({"12dRugNcdxK39288NjcDV4GX7rMsKCGn6B":
                        "address label"}))
        assert wallet._addresses[test_address]["label"] is None
        assert len(wallet._addresses) == number_of_addresses

        # Test it with a txid label
        wallet.import_address_labels(json.dumps({txid: "txid label"}))
        assert wallet._addresses[test_address]["label"] == "txid label"

        # The txid label should now be replaced by the address label
        wallet.import_address_labels(
            json.dumps({test_address: "address label"}))
        assert wallet._addresses[test_address]["label"] == "address label"

        # Specter JSON
        wallet._addresses[test_address].set_label("some_fancy_label_json")
        specter_json = json.dumps(wallet.to_json(for_export=True))
        wallet._addresses[test_address].set_label("label_got_lost")
        wallet.import_address_labels(specter_json)
        assert wallet._addresses[test_address][
            "label"] == "some_fancy_label_json"

        # Specter CSV
        csv_string = """Index,Address,Type,Label,Used,UTXO,Amount (BTC)
        0,2NCSZrX49HHyzUy6oj8ggm9WD19hFvjzzou,receive,some_fancy_label_csv,Yes,0,0"""
        wallet._addresses[test_address].set_label("label_got_lost")
        wallet.import_address_labels(csv_string)
        assert wallet._addresses[test_address][
            "label"] == "some_fancy_label_csv"

    finally:
        # Clean up
        bitcoind_controller.stop_bitcoind()
Esempio n. 9
0
def test_wallet_labeling(bitcoin_regtest, devices_filled_data_folder,
                         device_manager):
    wm = WalletManager(
        200100,
        devices_filled_data_folder,
        bitcoin_regtest.get_rpc(),
        "regtest",
        device_manager,
        allow_threading=False,
    )
    # A wallet-creation needs a device
    device = device_manager.get_by_alias("specter")
    key = Key.from_json({
        "derivation":
        "m/48h/1h/0h/2h",
        "original":
        "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
        "fingerprint":
        "08686ac6",
        "type":
        "wsh",
        "xpub":
        "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL",
    })
    wallet = wm.create_wallet("a_second_test_wallet", 1, "wpkh", [key],
                              [device])

    address = wallet.address
    assert wallet.getlabel(address) == "Address #0"
    wallet.setlabel(address, "Random label")
    assert wallet.getlabel(address) == "Random label"

    wallet.rpc.generatetoaddress(20, address)

    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(100, random_address)

    # update utxo
    wallet.getdata()
    # update balance
    wallet.update_balance()

    address_balance = wallet.fullbalance
    assert len(wallet.full_utxo) == 20

    print(wallet.full_utxo[4])
    # Something like:
    # { 'txid': 'fab823558781745179916b4bfdfd65b382bfc0e70e85188f1b9538604202f537',
    #   'vout': 0, 'address': 'bcrt1qmlrraffw0evkjy2yrxmt263ksgfgv2gqhcddrt',
    #   'label': 'Random label', 'scriptPubKey': '0014dfc63ea52e7e5969114419b6b56a368212862900',
    #   'amount': 50.0, 'confirmations': 101, 'spendable': False, 'solvable': True,
    #   'desc': "wpkh([08686ac6/48'/1'/0'/2'/0/0]02fa445808af849209038f422a22e335754fa07a2ece42fc483660606dcda3e0e9)#8q60z40m",
    #   'safe': True, 'time': 1637091575, 'category': 'generate', 'locked': False
    # }

    new_address = wallet.getnewaddress()
    wallet.setlabel(new_address, "")
    wallet.rpc.generatetoaddress(20, new_address)

    random_address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r"
    wallet.rpc.generatetoaddress(100, random_address)

    wallet.getdata()
    wallet.update_balance()

    assert len(wallet.full_utxo) == 40

    wallet.setlabel(new_address, "")
    third_address = wallet.getnewaddress()

    wallet.getdata()
    assert sorted(wallet.addresses) == sorted(
        [address, new_address, third_address])
Esempio n. 10
0
def test_WalletManager_check_duplicate_keys(empty_data_folder):
    wm = WalletManager(
        200100,
        empty_data_folder,
        None,
        "regtest",
        None,
        allow_threading=False,
    )
    key1 = Key(
        "[f3e6eaff/84h/0h/0h]xpub6C5cCQfycZrPJnNg6cDdUU5efJrab8thRQDBxSSB4gP2J3xGdWu8cqiLvPZkejtuaY9LursCn6Es9PqHgLhBktW8217BomGDVBAJjUms8iG",
        "f3e6eaff",
        "84h/0h/0h",
        "",
        None,
        "xpub6C5cCQfycZrPJnNg6cDdUU5efJrab8thRQDBxSSB4gP2J3xGdWu8cqiLvPZkejtuaY9LursCn6Es9PqHgLhBktW8217BomGDVBAJjUms8iG",
    )
    key2 = Key(
        "[1ef4e492/49h/0h/0h]xpub6CRWp2zfwRYsVTuT2p96hKE2UT4vjq9gwvW732KWQjwoG7v6NCXyaTdz7NE5yDxsd72rAGK7qrjF4YVrfZervsJBjsXxvTL98Yhc7poBk7K",
        "1ef4e492",
        "m/49h/0h/0h",
        "sh-wpkh",
        None,
        "xpub6CRWp2zfwRYsVTuT2p96hKE2UT4vjq9gwvW732KWQjwoG7v6NCXyaTdz7NE5yDxsd72rAGK7qrjF4YVrfZervsJBjsXxvTL98Yhc7poBk7K",
    )
    key3 = Key(
        "[1ef4e492/49h/0h/0h]zpub6qk8ok1ouvwM1NkumKnsteGf1F9UUNshFdFdXEDwph8nQFaj8qEFry2cxoUveZCkPpNxQp4KhQwxuy4R7jXDMMsKkgW2yauC2dHbWYnr2Ee",
        "1ef4e492",
        "m/49h/0h/0h",
        "sh-wpkh",
        None,
        "zpub6qk8ok1ouvwM1NkumKnsteGf1F9UUNshFdFdXEDwph8nQFaj8qEFry2cxoUveZCkPpNxQp4KhQwxuy4R7jXDMMsKkgW2yauC2dHbWYnr2Ee",
    )

    key4 = Key(
        "[6ea15da6/49h/0h/0h]xpub6BtcNhqbaFaoC3oEfKky3Sm22pF48U2jmAf78cB3wdAkkGyAgmsVrgyt1ooSt3bHWgzsdUQh2pTJ867yTeUAMmFDKNSBp8J7WPmp7Df7zjv",
        "6ea15da6",
        "m/49h/0h/0h",
        "sh-wpkh",
        None,
        "xpub6BtcNhqbaFaoC3oEfKky3Sm22pF48U2jmAf78cB3wdAkkGyAgmsVrgyt1ooSt3bHWgzsdUQh2pTJ867yTeUAMmFDKNSBp8J7WPmp7Df7zjv",
    )

    key5 = Key(
        "[6ea15da6/49h/0h/0h]xpub6BtcNhqbaFaoG3xcuncx9xzL3X38FuWXdcdvsdG5Q99Cb4EgeVYPEYaVpX28he6472gEsCokg8v9oMVRTrZNe5LHtGSPcC5ofehYkhD1Kxy",
        "6ea15da6",
        "m/49h/0h/1h",
        "sh-wpkh",
        None,  # slightly different ypub than key4
        "xpub6BtcNhqbaFaoG3xcuncx9xzL3X38FuWXdcdvsdG5Q99Cb4EgeVYPEYaVpX28he6472gEsCokg8v9oMVRTrZNe5LHtGSPcC5ofehYkhD1Kxy",
    )

    # Case 1: Identical keys
    keys = [key1, key1]
    with pytest.raises(SpecterError):
        wm._check_duplicate_keys(keys)
    # Case 2: different keys
    # key2 and 3 are different as they don't have the same xpub. See #1500 for discussion
    keys = [key1, key2, key3]  # key2 xpub is the same than key3 zpub
    with pytest.raises(SpecterError):
        wm._check_duplicate_keys(keys)

    keys = [key4, key5]
    wm._check_duplicate_keys(keys)