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])
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()
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()
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])
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)