You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1065 lines
42 KiB

'''Wallet functionality tests.'''
import datetime
import os
import json
from binascii import hexlify, unhexlify
import pytest
import jmbitcoin as btc
from commontest import ensure_bip65_activated
from jmbase import get_log, hextobin, bintohex
from jmclient import load_test_config, jm_single, BaseWallet, \
SegwitLegacyWallet,BIP32Wallet, BIP49Wallet, LegacyWallet,\
VolatileStorage, get_network, cryptoengine, WalletError,\
SegwitWallet, WalletService, SegwitWalletFidelityBonds,\
create_wallet, open_test_wallet_maybe, open_wallet, \
FidelityBondMixin, FidelityBondWatchonlyWallet,\
wallet_gettimelockaddress, UnknownAddressForLabel
from test_blockchaininterface import sync_test_wallet
from freezegun import freeze_time
pytestmark = pytest.mark.usefixtures("setup_regtest_bitcoind")
testdir = os.path.dirname(os.path.realpath(__file__))
test_create_wallet_filename = "testwallet_for_create_wallet_test"
test_cache_cleared_filename = "testwallet_for_cache_clear_test"
log = get_log()
def signed_tx_is_segwit(tx):
return tx.has_witness()
def assert_segwit(tx):
assert signed_tx_is_segwit(tx)
def assert_not_segwit(tx):
assert not signed_tx_is_segwit(tx)
def get_populated_wallet(amount=10**8, num=3):
storage = VolatileStorage()
SegwitLegacyWallet.initialize(storage, get_network())
wallet = SegwitLegacyWallet(storage)
# fund three wallet addresses at mixdepth 0
for i in range(num):
fund_wallet_addr(wallet, wallet.get_internal_addr(0), amount / 10**8)
return wallet
def fund_wallet_addr(wallet, addr, value_btc=1):
# special case, grab_coins returns hex from rpc:
txin_id = hextobin(jm_single().bc_interface.grab_coins(addr, value_btc))
txinfo = jm_single().bc_interface.get_transaction(txin_id)
txin = btc.CMutableTransaction.deserialize(btc.x(txinfo["hex"]))
utxo_in = wallet.add_new_utxos(txin, 1)
assert len(utxo_in) == 1
return list(utxo_in.keys())[0]
def get_bip39_vectors():
fh = open(os.path.join(testdir, 'bip39vectors.json'))
data = json.load(fh)['english']
fh.close()
return data
@pytest.mark.parametrize('entropy,mnemonic,key,xpriv', get_bip39_vectors())
def test_bip39_seeds(monkeypatch, setup_wallet, entropy, mnemonic, key, xpriv):
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
created_entropy = SegwitLegacyWallet.entropy_from_mnemonic(mnemonic)
assert entropy == hexlify(created_entropy).decode('ascii')
storage = VolatileStorage()
SegwitLegacyWallet.initialize(
storage, get_network(), entropy=created_entropy,
entropy_extension='TREZOR', max_mixdepth=4)
wallet = SegwitLegacyWallet(storage)
assert (mnemonic, b'TREZOR') == wallet.get_mnemonic_words()
assert key == hexlify(wallet._create_master_key()).decode('ascii')
# need to monkeypatch this, else we'll default to the BIP-49 path
monkeypatch.setattr(SegwitLegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
assert xpriv == wallet.get_bip32_priv_export()
def test_bip49_seed(monkeypatch, setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
master_xpriv = 'tprv8ZgxMBicQKsPe5YMU9gHen4Ez3ApihUfykaqUorj9t6FDqy3nP6eoXiAo2ssvpAjoLroQxHqr3R5nE3a5dU3DHTjTgJDd7zrbniJr6nrCzd'
account0_xpriv = 'tprv8gRrNu65W2Msef2BdBSUgFdRTGzC8EwVXnV7UGS3faeXtuMVtGfEdidVeGbThs4ELEoayCAzZQ4uUji9DUiAs7erdVskqju7hrBcDvDsdbY'
addr0_script_hash = '336caa13e08b96080a32b5d818d59b4ab3b36742'
entropy = SegwitLegacyWallet.entropy_from_mnemonic(mnemonic)
storage = VolatileStorage()
SegwitLegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=0)
wallet = SegwitLegacyWallet(storage)
assert (mnemonic, None) == wallet.get_mnemonic_words()
assert account0_xpriv == wallet.get_bip32_priv_export(0)
assert addr0_script_hash == hexlify(wallet.get_external_script(0)[2:-1]).decode('ascii')
# FIXME: is this desired behaviour? BIP49 wallet will not return xpriv for
# the root key but only for key after base path
monkeypatch.setattr(SegwitLegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
assert master_xpriv == wallet.get_bip32_priv_export()
def test_bip32_test_vector_1(monkeypatch, setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
entropy = unhexlify('000102030405060708090a0b0c0d0e0f')
storage = VolatileStorage()
LegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=0)
# test vector 1 is using hardened derivation for the account/mixdepth level
monkeypatch.setattr(LegacyWallet, '_get_mixdepth_from_path',
BIP49Wallet._get_mixdepth_from_path)
monkeypatch.setattr(LegacyWallet, '_get_bip32_mixdepth_path_level',
BIP49Wallet._get_bip32_mixdepth_path_level)
monkeypatch.setattr(LegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
monkeypatch.setattr(LegacyWallet, '_create_master_key',
BIP32Wallet._create_master_key)
wallet = LegacyWallet(storage)
assert wallet.get_bip32_priv_export() == 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
assert wallet.get_bip32_pub_export() == 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8'
assert wallet.get_bip32_priv_export(0) == 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7'
assert wallet.get_bip32_pub_export(0) == 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw'
assert wallet.get_bip32_priv_export(0, 1) == 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs'
assert wallet.get_bip32_pub_export(0, 1) == 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ'
# there are more test vectors but those don't match joinmarket's wallet
# structure, hence they make litte sense to test here
def test_bip32_test_vector_2(monkeypatch, setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
entropy = unhexlify('fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542')
storage = VolatileStorage()
LegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=0)
monkeypatch.setattr(LegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
monkeypatch.setattr(LegacyWallet, '_create_master_key',
BIP32Wallet._create_master_key)
wallet = LegacyWallet(storage)
assert wallet.get_bip32_priv_export() == 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U'
assert wallet.get_bip32_pub_export() == 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB'
assert wallet.get_bip32_priv_export(0) == 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt'
assert wallet.get_bip32_pub_export(0) == 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH'
# there are more test vectors but those don't match joinmarket's wallet
# structure, hence they make litte sense to test here
def test_bip32_test_vector_3(monkeypatch, setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
entropy = unhexlify('4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be')
storage = VolatileStorage()
LegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=0)
# test vector 3 is using hardened derivation for the account/mixdepth level
monkeypatch.setattr(LegacyWallet, '_get_mixdepth_from_path',
BIP49Wallet._get_mixdepth_from_path)
monkeypatch.setattr(LegacyWallet, '_get_bip32_mixdepth_path_level',
BIP49Wallet._get_bip32_mixdepth_path_level)
monkeypatch.setattr(LegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
monkeypatch.setattr(LegacyWallet, '_create_master_key',
BIP32Wallet._create_master_key)
wallet = LegacyWallet(storage)
assert wallet.get_bip32_priv_export() == 'xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6'
assert wallet.get_bip32_pub_export() == 'xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13'
assert wallet.get_bip32_priv_export(0) == 'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L'
assert wallet.get_bip32_pub_export(0) == 'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y'
@pytest.mark.parametrize('mixdepth,internal,index,address,wif', [
[0, BaseWallet.ADDRESS_TYPE_EXTERNAL, 0, 'mpCX9EbdXpcrKMtjEe1fqFhvzctkfzMYTX', 'cVqtSSoVxFyPqTRGfeESi31uCYfgTF4tGWRtGeVs84fzybiX5TPk'],
[0, BaseWallet.ADDRESS_TYPE_EXTERNAL, 5, 'mtj85a3pFppRhrxNcFig1k7ECshrZjJ9XC', 'cMsFXc4TRw9PTcCTv7x9mr88rDeGXBTLEV67mKaw2cxCkjkhL32G'],
[0, BaseWallet.ADDRESS_TYPE_INTERNAL, 3, 'n1EaQuqvTRm719hsSJ7yRsj49JfoG1C86q', 'cUgSTqnAtvYoQRXCYy4wCFfaks2Zrz1d55m6mVhFyVhQbkDi7JGJ'],
[2, BaseWallet.ADDRESS_TYPE_INTERNAL, 2, 'mfxkBk7uDhmF5PJGS9d1NonGiAxPwJqQP4', 'cPcZXSiXPuS5eiT4oDrDKi1mFumw5D1RcWzK2gkGdEHjEz99eyXn']
])
def test_bip32_addresses_p2pkh(monkeypatch, setup_wallet, mixdepth, internal, index, address, wif):
"""
Test with a random but fixed entropy
"""
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
entropy = unhexlify('2e0339ba89b4a1272cdf78b27ee62669ee01992a59e836e2807051be128ca817')
storage = VolatileStorage()
LegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=3)
monkeypatch.setattr(LegacyWallet, '_get_bip32_base_path',
BIP32Wallet._get_bip32_base_path)
monkeypatch.setattr(LegacyWallet, '_create_master_key',
BIP32Wallet._create_master_key)
wallet = LegacyWallet(storage)
# wallet needs to know about all intermediate keys
for i in range(index + 1):
wallet.get_new_script(mixdepth, internal)
assert wif == wallet.get_wif(mixdepth, internal, index)
assert address == wallet.get_addr(mixdepth, internal, index)
@pytest.mark.parametrize('mixdepth,internal,index,address,wif', [
[0, 0, 0, '2MzY5yyonUY7zpHspg7jB7WQs1uJxKafQe4', 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM'],
[0, 0, 5, '2MsKvqPGStp3yXT8UivuAaGwfPzT7xYwSWk', 'cSo3h7nRuV4fwhVPXeTDJx6cBCkjAzS9VM8APXViyjoSaMq85ZKn'],
[0, 1, 3, '2N7k6wiQqkuMaApwGhk3HKrifprUSDydqUv', 'cTwq3UsZa8STVmwZR94dDphgqgdLFeuaRFD1Ea44qjbjFfKEb1n5'],
[2, 1, 2, '2MtE6gzHgmEXeWzKsmCJFEqkrpNuBDvoRnz', 'cPV8FZuCvrRpk4RhmhpjnSucHhaQZUan4Vbyo1NVQtuAxurW9grb']
])
def test_bip32_addresses_p2sh_p2wpkh(setup_wallet, mixdepth, internal, index, address, wif):
"""
Test with a random but fixed entropy
"""
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
entropy = unhexlify('2e0339ba89b4a1272cdf78b27ee62669ee01992a59e836e2807051be128ca817')
storage = VolatileStorage()
SegwitLegacyWallet.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=3)
wallet = SegwitLegacyWallet(storage)
# wallet needs to know about all intermediate keys
for i in range(index + 1):
wallet.get_new_script(mixdepth, internal)
assert wif == wallet.get_wif(mixdepth, internal, index)
assert address == wallet.get_addr(mixdepth, internal, index)
@pytest.mark.parametrize('timenumber,address,wif', [
[0, 'bcrt1qgysu2eynn6klarz200ctgev7gqhhp7hwsdaaec3c7h0ltmc3r68q87c2d3', 'cVASAS6bpC5yctGmnsKaDz7D8CxEwccUtpjSNBQzeV2fw8ox8RR9'],
[50, 'bcrt1q0cnscj0hlf6xqzlqwk7swngd3kmvd6unn49j9h4zgg68kg8fd7gq0r87lf', 'cMtnaLzC2EW3URnmAapRnPQECGwGruxqXJpAnuRjKup3pkWfrxRE'],
[1, 'bcrt1q26vw0q28rz2r2ktehp8w5yfzkzskrc4fxqdhzjy0f88kzhjvlfrs7fyas6', 'cU8G1YAAxGZMqNsXxApBAahb8pbxhxryDshFdX5eRT9FV4gHNVXT']
])
def test_bip32_timelocked_addresses(setup_wallet, timenumber, address, wif):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
entropy = unhexlify('2e0339ba89b4a1272cdf78b27ee62669ee01992a59e836e2807051be128ca817')
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=1)
wallet = SegwitWalletFidelityBonds(storage)
mixdepth = FidelityBondMixin.FIDELITY_BOND_MIXDEPTH
address_type = FidelityBondMixin.BIP32_TIMELOCK_ID
assert address == wallet.get_addr(mixdepth, address_type, timenumber)
assert wif == wallet.get_wif_path(wallet.get_path(mixdepth, address_type, timenumber))
@pytest.mark.parametrize('timenumber,locktime_string', [
[0, "2020-01"],
[20, "2021-09"],
[100, "2028-05"],
[150, "2032-07"],
[350, "2049-03"]
])
@freeze_time("2019-12")
def test_gettimelockaddress_method(setup_wallet, timenumber, locktime_string):
jm_single().config.set("BLOCKCHAIN", "network", "mainnet")
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(storage, get_network())
wallet = SegwitWalletFidelityBonds(storage)
m = FidelityBondMixin.FIDELITY_BOND_MIXDEPTH
address_type = FidelityBondMixin.BIP32_TIMELOCK_ID
script = wallet.get_script(m, address_type, timenumber)
addr = wallet.script_to_addr(script)
addr_from_method = wallet_gettimelockaddress(wallet, locktime_string)
assert addr == addr_from_method
@freeze_time("2021-01")
def test_gettimelockaddress_in_past(setup_wallet):
jm_single().config.set("BLOCKCHAIN", "network", "mainnet")
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(storage, get_network())
wallet = SegwitWalletFidelityBonds(storage)
assert wallet_gettimelockaddress(wallet, "2020-01") == ""
assert wallet_gettimelockaddress(wallet, "2021-01") == ""
assert wallet_gettimelockaddress(wallet, "2021-02") != ""
@pytest.mark.parametrize('index,wif', [
[0, 'cVQbz7DB5JQ1TGsg9Dbm32VtJbXBHaj39Yc9QLkaGpRgXcibHTDH'],
[9, 'cULqe2sYZ4z8jZTGybr2Bzf4EyiT5Ts6wAE3mvCUofRuTVsofR8N'],
[50, 'cQNp7cQbrwjWuxmbkZF8ax9ogmTuWp3Ykb9LEpainhRTJXYc8Deu']
])
def test_bip32_burn_keys(setup_wallet, index, wif):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
entropy = unhexlify('2e0339ba89b4a1272cdf78b27ee62669ee01992a59e836e2807051be128ca817')
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(
storage, get_network(), entropy=entropy, max_mixdepth=1)
wallet = SegwitWalletFidelityBonds(storage)
mixdepth = FidelityBondMixin.FIDELITY_BOND_MIXDEPTH
address_type = FidelityBondMixin.BIP32_BURN_ID
#advance index_cache enough
wallet.set_next_index(mixdepth, address_type, index, force=True)
assert wif == wallet.get_wif_path(wallet.get_path(mixdepth, address_type, index))
def test_import_key(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
storage = VolatileStorage()
SegwitLegacyWallet.initialize(storage, get_network())
wallet = SegwitLegacyWallet(storage)
wallet.import_private_key(
0, 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM')
wallet.import_private_key(
1, 'cVqtSSoVxFyPqTRGfeESi31uCYfgTF4tGWRtGeVs84fzybiX5TPk')
with pytest.raises(WalletError):
wallet.import_private_key(
1, 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM')
# test persist imported keys
wallet.save()
data = storage.file_data
del wallet
del storage
storage = VolatileStorage(data=data)
wallet = SegwitLegacyWallet(storage)
imported_paths_md0 = list(wallet.yield_imported_paths(0))
imported_paths_md1 = list(wallet.yield_imported_paths(1))
assert len(imported_paths_md0) == 1
assert len(imported_paths_md1) == 1
# verify imported addresses
assert wallet.get_address_from_path(imported_paths_md0[0]) == '2MzY5yyonUY7zpHspg7jB7WQs1uJxKafQe4'
assert wallet.get_address_from_path(imported_paths_md1[0]) == '2MwbXnJrPP4rnwpgRhvNPP44J6tMokDexZB'
# test remove key
wallet.remove_imported_key(path=imported_paths_md0[0])
assert not list(wallet.yield_imported_paths(0))
assert wallet.get_details(imported_paths_md1[0]) == (1, 'imported', 0)
@pytest.mark.parametrize('wif, type_check', [
['cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM', assert_segwit]
])
def test_signing_imported(setup_wallet, wif, type_check):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
storage = VolatileStorage()
SegwitLegacyWallet.initialize(storage, get_network())
wallet = SegwitLegacyWallet(storage)
MIXDEPTH = 0
path = wallet.import_private_key(MIXDEPTH, wif)
utxo = fund_wallet_addr(wallet, wallet.get_address_from_path(path))
# The dummy output is constructed as an unspendable p2sh:
tx = btc.mktx([utxo],
[{"address": str(btc.CCoinAddress.from_scriptPubKey(
btc.CScript(b"\x00").to_p2sh_scriptPubKey())),
"value": 10**8 - 9000}])
script = wallet.get_script_from_path(path)
success, msg = wallet.sign_tx(tx, {0: (script, 10**8)})
assert success, msg
type_check(tx)
txout = jm_single().bc_interface.pushtx(tx.serialize())
assert txout
@pytest.mark.parametrize('wallet_cls,type_check', [
[LegacyWallet, assert_not_segwit],
[SegwitLegacyWallet, assert_segwit],
[SegwitWallet, assert_segwit],
])
def test_signing_simple(setup_wallet, wallet_cls, type_check):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
storage = VolatileStorage()
wallet_cls.initialize(storage, get_network(), entropy=b"\xaa"*16)
wallet = wallet_cls(storage)
utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0))
# The dummy output is constructed as an unspendable p2sh:
tx = btc.mktx([utxo],
[{"address": str(btc.CCoinAddress.from_scriptPubKey(
btc.CScript(b"\x00").to_p2sh_scriptPubKey())),
"value": 10**8 - 9000}])
script = wallet.get_script(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 0)
success, msg = wallet.sign_tx(tx, {0: (script, 10**8)})
assert success, msg
type_check(tx)
txout = jm_single().bc_interface.pushtx(tx.serialize())
assert txout
# note that address validation is tested separately;
# this test functions only to make sure that given a valid
# taproot address, we can actually spend to it
@pytest.mark.parametrize('hexspk', [
"512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
"5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",
"5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",
])
def test_spend_to_p2traddr(setup_wallet, hexspk):
storage = VolatileStorage()
SegwitWallet.initialize(storage, get_network(), entropy=b"\xaa"*16)
wallet = SegwitWallet(storage)
utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0))
sPK = btc.CScript(hextobin(hexspk))
tx = btc.mktx([utxo],
[{"address": str(btc.CCoinAddress.from_scriptPubKey(sPK)),
"value": 10**8 - 9000}])
script = wallet.get_script(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 0)
success, msg = wallet.sign_tx(tx, {0: (script, 10**8)})
assert success, msg
txout = jm_single().bc_interface.pushtx(tx.serialize())
assert txout
# probably unnecessary, but since we are sanity checking:
# does the output of the in-mempool tx have the sPK we expect?
txid = tx.GetTxid()[::-1]
txres = btc.CTransaction.deserialize(hextobin(jm_single().bc_interface._rpc(
"getrawtransaction", [bintohex(txid), True])["hex"]))
assert txres.vout[0].scriptPubKey == sPK
assert txres.vout[0].nValue == 10**8 - 9000
def test_timelocked_output_signing(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
ensure_bip65_activated()
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(storage, get_network())
wallet = SegwitWalletFidelityBonds(storage)
timenumber = 0
script = wallet.get_script(
FidelityBondMixin.FIDELITY_BOND_MIXDEPTH,
FidelityBondMixin.BIP32_TIMELOCK_ID, timenumber)
utxo = fund_wallet_addr(wallet, wallet.script_to_addr(script))
timestamp = wallet._time_number_to_timestamp(timenumber)
tx = btc.mktx([utxo], [{"address": str(btc.CCoinAddress.from_scriptPubKey(
btc.standard_scripthash_scriptpubkey(btc.Hash160(b"\x00")))),
"value":10**8 - 9000}], locktime=timestamp+1)
success, msg = wallet.sign_tx(tx, {0: (script, 10**8)})
assert success, msg
txout = jm_single().bc_interface.pushtx(tx.serialize())
assert txout
def test_get_bbm(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
amount = 10**8
num_tx = 3
wallet = get_populated_wallet(amount, num_tx)
# disable a utxo and check we can correctly report
# balance with the disabled flag off:
utxo_1 = list(wallet._utxos.get_utxos_at_mixdepth(0).keys())[0]
wallet.disable_utxo(*utxo_1)
balances = wallet.get_balance_by_mixdepth(include_disabled=True)
assert balances[0] == num_tx * amount
balances = wallet.get_balance_by_mixdepth()
assert balances[0] == (num_tx - 1) * amount
wallet.toggle_disable_utxo(*utxo_1)
balances = wallet.get_balance_by_mixdepth()
assert balances[0] == num_tx * amount
def test_add_utxos(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
amount = 10**8
num_tx = 3
wallet = get_populated_wallet(amount, num_tx)
balances = wallet.get_balance_by_mixdepth()
assert balances[0] == num_tx * amount
for md in range(1, wallet.max_mixdepth + 1):
assert balances[md] == 0
utxos = wallet.get_utxos_by_mixdepth()
assert len(utxos[0]) == num_tx
for md in range(1, wallet.max_mixdepth + 1):
assert not utxos[md]
with pytest.raises(Exception):
# no funds in mixdepth
wallet.select_utxos(1, amount)
with pytest.raises(Exception):
# not enough funds
wallet.select_utxos(0, amount * (num_tx + 1))
wallet.reset_utxos()
assert wallet.get_balance_by_mixdepth()[0] == 0
def test_select_utxos(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
amount = 10**8
wallet = get_populated_wallet(amount)
utxos = wallet.select_utxos(0, amount // 2)
assert len(utxos) == 1
utxos = list(utxos.keys())
more_utxos = wallet.select_utxos(0, int(amount * 1.5), utxo_filter=utxos)
assert len(more_utxos) == 2
assert utxos[0] not in more_utxos
def test_add_new_utxos(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
wallet = get_populated_wallet(num=1)
scripts = [wallet.get_new_script(x,
BaseWallet.ADDRESS_TYPE_INTERNAL) for x in range(3)]
tx_scripts = list(scripts)
tx = btc.mktx(
[(b"\x00"*32, 2)],
[{"address": wallet.script_to_addr(s),
"value": 10**8} for s in tx_scripts])
added = wallet.add_new_utxos(tx, 1)
assert len(added) == len(scripts)
added_scripts = {x['script'] for x in added.values()}
for s in scripts:
assert s in added_scripts
balances = wallet.get_balance_by_mixdepth()
assert balances[0] == 2 * 10**8
assert balances[1] == 10**8
assert balances[2] == 10**8
assert len(balances) == wallet.max_mixdepth + 1
def test_remove_old_utxos(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
wallet = get_populated_wallet()
# add some more utxos to mixdepth 1
for i in range(3):
txin = jm_single().bc_interface.grab_coins(
wallet.get_internal_addr(1), 1)
wallet.add_utxo(btc.x(txin), 0, wallet.get_script(1,
BaseWallet.ADDRESS_TYPE_INTERNAL, i), 10**8, 1)
inputs = wallet.select_utxos(0, 10**8)
inputs.update(wallet.select_utxos(1, 2 * 10**8))
assert len(inputs) == 3
tx_inputs = list(inputs.keys())
tx_inputs.append((b'\x12'*32, 6))
tx = btc.mktx(tx_inputs,
[{"address": "2N9gfkUsFW7Kkb1Eurue7NzUxUt7aNJiS1U",
"value": 3 * 10**8 - 1000}])
removed = wallet.remove_old_utxos(tx)
assert len(removed) == len(inputs)
for txid in removed:
assert txid in inputs
balances = wallet.get_balance_by_mixdepth()
assert balances[0] == 2 * 10**8
assert balances[1] == 10**8
assert balances[2] == 0
assert len(balances) == wallet.max_mixdepth + 1
def test_address_labels(setup_wallet):
wallet = get_populated_wallet(num=2)
addr1 = wallet.get_internal_addr(0)
addr2 = wallet.get_internal_addr(1)
assert wallet.get_address_label(addr2) is None
assert wallet.get_address_label(addr2) is None
wallet.set_address_label(addr1, "test")
# utf-8 characters here are on purpose, to test utf-8 encoding / decoding
wallet.set_address_label(addr2, "glāžšķūņu rūķīši")
assert wallet.get_address_label(addr1) == "test"
assert wallet.get_address_label(addr2) == "glāžšķūņu rūķīši"
wallet.set_address_label(addr1, "")
wallet.set_address_label(addr2, None)
assert wallet.get_address_label(addr2) is None
assert wallet.get_address_label(addr2) is None
with pytest.raises(UnknownAddressForLabel):
wallet.get_address_label("2MzY5yyonUY7zpHspg7jB7WQs1uJxKafQe4")
wallet.set_address_label("2MzY5yyonUY7zpHspg7jB7WQs1uJxKafQe4",
"test")
# we no longer decode addresses just to see if we know about them,
# so we won't get a CCoinAddressError for invalid addresses
#with pytest.raises(CCoinAddressError):
wallet.get_address_label("badaddress")
wallet.set_address_label("badaddress", "test")
def test_initialize_twice(setup_wallet):
wallet = get_populated_wallet(num=0)
storage = wallet._storage
with pytest.raises(WalletError):
SegwitLegacyWallet.initialize(storage, get_network())
def test_is_known(setup_wallet):
wallet = get_populated_wallet(num=0)
script = wallet.get_new_script(1, BaseWallet.ADDRESS_TYPE_INTERNAL)
addr = wallet.get_external_addr(2)
assert wallet.is_known_script(script)
assert wallet.is_known_addr(addr)
assert wallet.is_known_addr(wallet.script_to_addr(script))
assert wallet.is_known_script(wallet.addr_to_script(addr))
assert not wallet.is_known_script(b'\x12' * len(script))
assert not wallet.is_known_addr('2MzY5yyonUY7zpHspg7jB7WQs1uJxKafQe4')
def test_wallet_save(setup_wallet):
wallet = get_populated_wallet()
script = wallet.get_external_script(1)
wallet.save()
storage = wallet._storage
data = storage.file_data
del wallet
del storage
storage = VolatileStorage(data=data)
wallet = SegwitLegacyWallet(storage)
assert wallet.get_next_unused_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL) == 3
assert wallet.get_next_unused_index(0, BaseWallet.ADDRESS_TYPE_EXTERNAL) == 0
assert wallet.get_next_unused_index(1, BaseWallet.ADDRESS_TYPE_INTERNAL) == 0
assert wallet.get_next_unused_index(1, BaseWallet.ADDRESS_TYPE_EXTERNAL) == 1
assert wallet.is_known_script(script)
def test_set_next_index(setup_wallet):
wallet = get_populated_wallet()
assert wallet.get_next_unused_index(0,
BaseWallet.ADDRESS_TYPE_INTERNAL) == 3
with pytest.raises(Exception):
# cannot advance index without force=True
wallet.set_next_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 5)
wallet.set_next_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 1)
assert wallet.get_next_unused_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL) == 1
wallet.set_next_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 20, force=True)
assert wallet.get_next_unused_index(0, BaseWallet.ADDRESS_TYPE_INTERNAL) == 20
script = wallet.get_new_script(0, BaseWallet.ADDRESS_TYPE_INTERNAL)
path = wallet.script_to_path(script)
index = wallet.get_details(path)[2]
assert index == 20
def test_path_repr(setup_wallet):
wallet = get_populated_wallet()
path = wallet.get_path(2, BIP32Wallet.ADDRESS_TYPE_EXTERNAL, 0)
path_repr = wallet.get_path_repr(path)
path_new = wallet.path_repr_to_path(path_repr)
assert path_new == path
def test_path_repr_imported(setup_wallet):
wallet = get_populated_wallet(num=0)
path = wallet.import_private_key(
0, 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM')
path_repr = wallet.get_path_repr(path)
path_new = wallet.path_repr_to_path(path_repr)
assert path_new == path
@pytest.mark.parametrize('timenumber,timestamp', [
[0, 1577836800],
[50, 1709251200],
[300, 2366841600],
[1000, None], #too far in the future
[-1, None] #before epoch
])
def test_timenumber_to_timestamp(setup_wallet, timenumber, timestamp):
try:
implied_timestamp = FidelityBondMixin._time_number_to_timestamp(
timenumber)
assert implied_timestamp == timestamp
except ValueError:
#None means the timenumber is intentionally invalid
assert timestamp == None
@pytest.mark.parametrize('timestamp,timenumber', [
[1577836800, 0],
[1709251200, 50],
[2366841600, 300],
[1577836801, None], #not exactly midnight on first of month
[4133980800, None], #too far in future
[1575158400, None] #before epoch
])
def test_timestamp_to_timenumber(setup_wallet, timestamp, timenumber):
try:
implied_timenumber = FidelityBondMixin.timestamp_to_time_number(
timestamp)
assert implied_timenumber == timenumber
except ValueError:
assert timenumber == None
def test_wrong_wallet_cls(setup_wallet):
storage = VolatileStorage()
SegwitLegacyWallet.initialize(storage, get_network())
wallet = SegwitLegacyWallet(storage)
wallet.save()
data = storage.file_data
del wallet
del storage
storage = VolatileStorage(data=data)
with pytest.raises(Exception):
LegacyWallet(storage)
def test_wallet_id(setup_wallet):
storage1 = VolatileStorage()
SegwitLegacyWallet.initialize(storage1, get_network())
wallet1 = SegwitLegacyWallet(storage1)
storage2 = VolatileStorage()
LegacyWallet.initialize(storage2, get_network(), entropy=wallet1._entropy)
wallet2 = LegacyWallet(storage2)
assert wallet1.get_wallet_id() != wallet2.get_wallet_id()
storage2 = VolatileStorage()
SegwitLegacyWallet.initialize(storage2, get_network(),
entropy=wallet1._entropy)
wallet2 = SegwitLegacyWallet(storage2)
assert wallet1.get_wallet_id() == wallet2.get_wallet_id()
def test_cache_cleared(setup_wallet):
# test plan:
# 1. create a new wallet and sync from scratch
# 2. read its cache as an object
# 3. close the wallet, reopen it, sync it.
# 4. corrupt its cache and save.
# 5. Re open the wallet with recoversync
# and check that the corrupted data is not present.
if os.path.exists(test_cache_cleared_filename):
os.remove(test_cache_cleared_filename)
wallet = create_wallet(test_cache_cleared_filename,
b"hunter2", 2, SegwitWallet)
# note: we use the WalletService as an encapsulation
# of the wallet here because we want to be able to sync,
# but we do not actually start the service and go into
# the monitoring loop.
wallet_service = WalletService(wallet)
# default fast sync, no coins, so no loop
wallet_service.sync_wallet()
wallet_service.update_blockheight()
# to get the cache to save, we need to
# use an address:
addr = wallet_service.get_new_addr(0,0)
jm_single().bc_interface.grab_coins(addr, 1.0)
wallet_service.transaction_monitor()
path_to_corrupt = list(wallet._cache.keys())[0]
# we'll just corrupt the first address and script:
entry_to_corrupt = wallet._cache[path_to_corrupt][b"84'"][b"1'"][b"0'"][b'0'][b'0']
entry_to_corrupt[b'A'] = "notanaddress"
entry_to_corrupt[b'S'] = "notascript"
wallet_service.wallet.save()
wallet_service.wallet.close()
jm_single().config.set("POLICY", "wallet_caching_disabled", "true")
wallet2 = open_wallet(test_cache_cleared_filename,
ask_for_password=False,
password=b"hunter2")
jm_single().config.set("POLICY", "wallet_caching_disabled", "false")
wallet_service2 = WalletService(wallet2)
while not wallet_service2.synced:
wallet_service2.sync_wallet(fast=False)
wallet_service.transaction_monitor()
# we ignored the corrupt cache?
assert wallet_service2.get_balance_at_mixdepth(0) == 10 ** 8
def test_addr_script_conversion(setup_wallet):
wallet = get_populated_wallet(num=1)
path = wallet.get_path(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 0)
script = wallet.get_script_from_path(path)
addr = wallet.script_to_addr(script)
assert script == wallet.addr_to_script(addr)
addr_path = wallet.addr_to_path(addr)
assert path == addr_path
def test_imported_key_removed(setup_wallet):
wif = 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM'
storage = VolatileStorage()
SegwitLegacyWallet.initialize(storage, get_network())
wallet = SegwitLegacyWallet(storage)
path = wallet.import_private_key(1, wif)
script = wallet.get_script_from_path(path)
assert wallet.is_known_script(script)
wallet.remove_imported_key(path=path)
assert not wallet.is_known_script(script)
with pytest.raises(WalletError):
wallet.get_script_from_path(path)
def test_wallet_mixdepth_simple(setup_wallet):
wallet = get_populated_wallet(num=0)
mixdepth = wallet.mixdepth
assert wallet.max_mixdepth == mixdepth
wallet.close()
storage_data = wallet._storage.file_data
new_wallet = type(wallet)(VolatileStorage(data=storage_data))
assert new_wallet.mixdepth == mixdepth
assert new_wallet.max_mixdepth == mixdepth
def test_wallet_mixdepth_increase(setup_wallet):
wallet = get_populated_wallet(num=0)
mixdepth = wallet.mixdepth
wallet.close()
storage_data = wallet._storage.file_data
new_mixdepth = mixdepth + 2
new_wallet = type(wallet)(
VolatileStorage(data=storage_data), mixdepth=new_mixdepth)
assert new_wallet.mixdepth == new_mixdepth
assert new_wallet.max_mixdepth == new_mixdepth
def test_wallet_mixdepth_decrease(setup_wallet):
wallet = get_populated_wallet(num=1)
# setup
max_mixdepth = wallet.max_mixdepth
assert max_mixdepth >= 1, "bad default value for mixdepth for this test"
utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(max_mixdepth), 1)
bci = jm_single().bc_interface
unspent_list = bci.listunspent(0)
# filter on label, but note (a) in certain circumstances (in-
# wallet transfer) it is possible for the utxo to be labeled
# with the external label, and (b) the wallet will know if it
# belongs or not anyway (is_known_addr):
our_unspent_list = [x for x in unspent_list if (
bci.is_address_labeled(x, wallet.get_wallet_name()))]
assert wallet.get_balance_by_mixdepth()[max_mixdepth] == 10**8
wallet.close()
storage_data = wallet._storage.file_data
# actual test
new_mixdepth = max_mixdepth - 1
new_wallet = type(wallet)(
VolatileStorage(data=storage_data), mixdepth=new_mixdepth)
assert new_wallet.max_mixdepth == max_mixdepth
assert new_wallet.mixdepth == new_mixdepth
sync_test_wallet(True, WalletService(new_wallet))
assert max_mixdepth not in new_wallet.get_balance_by_mixdepth()
assert max_mixdepth not in new_wallet.get_utxos_by_mixdepth()
# wallet.select_utxos will still return utxos from higher mixdepths
# because we explicitly ask for a specific mixdepth
assert utxo in new_wallet.select_utxos(max_mixdepth, 10**7)
def test_watchonly_wallet(setup_wallet):
jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(storage, get_network())
wallet = SegwitWalletFidelityBonds(storage)
paths = [
"m/84'/1'/0'/0/0",
"m/84'/1'/0'/1/0",
"m/84'/1'/0'/2/0:1577836800",
"m/84'/1'/0'/2/0:2314051200"
]
burn_path = "m/49'/1'/0'/3/0"
scripts = [wallet.get_script_from_path(wallet.path_repr_to_path(path))
for path in paths]
privkey, engine = wallet._get_key_from_path(wallet.path_repr_to_path(burn_path))
burn_pubkey = engine.privkey_to_pubkey(privkey)
master_pub_key = wallet.get_bip32_pub_export(
FidelityBondMixin.FIDELITY_BOND_MIXDEPTH)
watchonly_storage = VolatileStorage()
entropy = FidelityBondMixin.get_xpub_from_fidelity_bond_master_pub_key(
master_pub_key).encode()
FidelityBondWatchonlyWallet.initialize(watchonly_storage, get_network(),
entropy=entropy)
watchonly_wallet = FidelityBondWatchonlyWallet(watchonly_storage)
watchonly_scripts = [watchonly_wallet.get_script_from_path(
watchonly_wallet.path_repr_to_path(path)) for path in paths]
privkey, engine = wallet._get_key_from_path(wallet.path_repr_to_path(burn_path))
watchonly_burn_pubkey = engine.privkey_to_pubkey(privkey)
for script, watchonly_script in zip(scripts, watchonly_scripts):
assert script == watchonly_script
assert burn_pubkey == watchonly_burn_pubkey
def test_calculate_timelocked_fidelity_bond_value(setup_wallet):
EPSILON = 0.000001
YEAR = 60*60*24*356.25
#the function should be flat anywhere before the locktime ends
values = [FidelityBondMixin.calculate_timelocked_fidelity_bond_value(
utxo_value=100000000,
confirmation_time=0,
locktime=6*YEAR,
current_time=y*YEAR,
interest_rate=0.01
)
for y in range(4)
]
value_diff = [values[i] - values[i+1] for i in range(len(values)-1)]
for vd in value_diff:
assert abs(vd) < EPSILON
#after locktime, the value should go down
values = [FidelityBondMixin.calculate_timelocked_fidelity_bond_value(
utxo_value=100000000,
confirmation_time=0,
locktime=6*YEAR,
current_time=(6+y)*YEAR,
interest_rate=0.01
)
for y in range(5)
]
value_diff = [values[i+1] - values[i] for i in range(len(values)-1)]
for vrd in value_diff:
assert vrd < 0
#value of a bond goes up as the locktime goes up
values = [FidelityBondMixin.calculate_timelocked_fidelity_bond_value(
utxo_value=100000000,
confirmation_time=0,
locktime=y*YEAR,
current_time=0,
interest_rate=0.01
)
for y in range(5)
]
value_ratio = [values[i] / values[i+1] for i in range(len(values)-1)]
value_ratio_diff = [value_ratio[i] - value_ratio[i+1] for i in range(len(value_ratio)-1)]
for vrd in value_ratio_diff:
assert vrd < 0
#value of a bond locked into the far future is constant, clamped at the value of burned coins
values = [FidelityBondMixin.calculate_timelocked_fidelity_bond_value(
utxo_value=100000000,
confirmation_time=0,
locktime=(200+y)*YEAR,
current_time=0,
interest_rate=0.01
)
for y in range(5)
]
value_diff = [values[i] - values[i+1] for i in range(len(values)-1)]
for vd in value_diff:
assert abs(vd) < EPSILON
@pytest.mark.parametrize('password, wallet_cls', [
["hunter2", SegwitLegacyWallet],
["hunter2", SegwitWallet],
])
def test_create_wallet(setup_wallet, password, wallet_cls):
wallet_name = test_create_wallet_filename
password = password.encode("utf-8")
# test mainnet (we are not transacting)
btc.select_chain_params("bitcoin")
wallet = create_wallet(wallet_name, password, 4, wallet_cls)
mnemonic = wallet.get_mnemonic_words()[0]
firstkey = wallet.get_key_from_addr(wallet.get_addr(0,0,0))
print("Created mnemonic, firstkey: ", mnemonic, firstkey)
wallet.close()
# ensure that the wallet file created is openable with the password,
# and has the parameters that were claimed on creation:
new_wallet = open_test_wallet_maybe(wallet_name, "", 4,
password=password, ask_for_password=False)
assert new_wallet.get_mnemonic_words()[0] == mnemonic
assert new_wallet.get_key_from_addr(
new_wallet.get_addr(0,0,0)) == firstkey
os.remove(wallet_name)
btc.select_chain_params("bitcoin/regtest")
@pytest.mark.parametrize('wallet_cls', [
SegwitLegacyWallet, SegwitWallet, SegwitWalletFidelityBonds
])
def test_is_standard_wallet_script(setup_wallet, wallet_cls):
storage = VolatileStorage()
wallet_cls.initialize(
storage, get_network(), max_mixdepth=0)
wallet = wallet_cls(storage)
script = wallet.get_new_script(0, 1)
assert wallet.is_known_script(script)
path = wallet.script_to_path(script)
assert wallet.is_standard_wallet_script(path)
def test_is_standard_wallet_script_nonstandard(setup_wallet):
storage = VolatileStorage()
SegwitWalletFidelityBonds.initialize(
storage, get_network(), max_mixdepth=0)
wallet = SegwitWalletFidelityBonds(storage)
import_path = wallet.import_private_key(
0, 'cRAGLvPmhpzJNgdMT4W2gVwEW3fusfaDqdQWM2vnWLgXKzCWKtcM')
assert wallet.is_standard_wallet_script(import_path)
ts = wallet.datetime_to_time_number(
datetime.datetime.strptime("2021-07", "%Y-%m"))
tl_path = wallet.get_path(0, wallet.BIP32_TIMELOCK_ID, ts)
assert not wallet.is_standard_wallet_script(tl_path)
@pytest.fixture(scope='module')
def setup_wallet(request):
load_test_config()
btc.select_chain_params("bitcoin/regtest")
#see note in cryptoengine.py:
cryptoengine.BTC_P2WPKH.VBYTE = 100
jm_single().bc_interface.tick_forward_chain_interval = 2
def teardown():
if os.path.exists(test_create_wallet_filename):
os.remove(test_create_wallet_filename)
if os.path.exists(test_cache_cleared_filename):
os.remove(test_cache_cleared_filename)
request.addfinalizer(teardown)