# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# Address ownership tests.
#
import pytest, time, io, csv, os, json
from txn import fake_address
from base58 import encode_base58_checksum
from helpers import hash160, taptweak, addr_from_display_format
from bech32 import encode as bech32_encode
from bip32 import BIP32Node
from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR
from constants import simulator_fixed_xprv, simulator_fixed_tprv, addr_fmt_names
from charcodes import KEY_QR

@pytest.fixture
def wipe_cache(sim_exec):
    def doit():
        cmd = f'from ownership import OWNERSHIP; OWNERSHIP.wipe_all();'
        sim_exec(cmd)
    return doit


'''
    >>> [AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH]
        [14,       8,       26,            1,          7,         19]
'''
@pytest.mark.parametrize('addr_fmt', [
    AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR
])
@pytest.mark.parametrize('testnet', [ False, True] )
def test_negative(addr_fmt, testnet, sim_exec):
    # unit test, no UX
    addr = fake_address(addr_fmt, testnet)

    cmd = f'from ownership import OWNERSHIP; w,path=OWNERSHIP.search({addr!r}); '\
            'RV.write(repr([w.name, path]))'
    lst = sim_exec(cmd)

    assert 'Explained' in lst

@pytest.mark.parametrize('addr_fmt, chain', [
	(AF_CLASSIC, "XTN"),
	(AF_CLASSIC, "BTC"),
	(AF_P2WPKH, "XTN"),
	(AF_P2WPKH, "BTC"),
	(AF_P2WPKH_P2SH, "XTN"),
	(AF_P2WPKH_P2SH, "BTC"),
    (AF_P2TR, "XTN"),
    (AF_P2TR, "BTC"),

    # multisig - testnet only
	(AF_P2WSH, "XTN"),
	(AF_P2SH, "XTN"),
	(AF_P2WSH_P2SH, "XTN"),
])
@pytest.mark.parametrize('offset', [ 3, 760, 763] )
@pytest.mark.parametrize('subaccount', [ 0, 34] )
@pytest.mark.parametrize('change_idx', [ 0, 1] )
@pytest.mark.parametrize('from_empty', [ True, False] )
def test_positive(addr_fmt, offset, subaccount, chain, from_empty, change_idx,
    sim_exec, wipe_cache, use_testnet, goto_home, pick_menu_item,
    enter_number, press_cancel, settings_set, import_ms_wallet, clear_miniscript, is_q1,
):

    # API/Unit test, limited UX
    ms_addr_fmts = { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH }
    if (addr_fmt in ms_addr_fmts) and subaccount:
        raise pytest.skip('multisig with subaccount')

    if chain == "BTC":
        use_testnet(False)
        testnet = False
        if addr_fmt in ms_addr_fmts:
            # multisig jigs assume testnet
            raise pytest.skip('testnet only')

    coin_type = 0
    if chain == "XTN":
        use_testnet(True)
        coin_type = 1
        testnet = True

    wipe_cache()        # very different codepaths
    settings_set('accts', [])

    if addr_fmt in { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH }:
        from test_multisig import make_ms_address, HARD
        M, N = 1, 3

        expect_name = f'search-test-{addr_fmt}'
        clear_miniscript()
        keys = import_ms_wallet(M, N, name=expect_name, accept=True, addr_fmt=addr_fmt_names[addr_fmt])

        # iffy: no cosigner index in this wallet, so indicated that w/ path_mapper
        addr, scriptPubKey, script, details = make_ms_address(
            M, keys, addr_fmt=addr_fmt, testnet=int(testnet),
            is_change=change_idx, idx=offset
        )
        path = f'.../{change_idx}/{offset}'
    else:

        if addr_fmt == AF_CLASSIC:
            menu_item = expect_name = 'Classic P2PKH'
            path = "m/44h/{ct}h/{acc}h"
        elif addr_fmt == AF_P2WPKH_P2SH:
            menu_item = expect_name = 'P2SH-Segwit'
            path = "m/49h/{ct}h/{acc}h"
            clear_miniscript()
        elif addr_fmt == AF_P2WPKH:
            menu_item = expect_name = 'Segwit P2WPKH'
            path = "m/84h/{ct}h/{acc}h"
        elif addr_fmt == AF_P2TR:
            menu_item = expect_name = 'Taproot P2TR'
            path = "m/86h/{ct}h/{acc}h"
        else:
            raise ValueError(addr_fmt)

        path_prefix = path.format(ct=coin_type, acc=subaccount)
        path = path_prefix + f'/{change_idx}/{offset}'
        print(f'path = {path}')

        # see addr_vs_path
        mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv)
        sk = mk.subkey_for_path(path)

        if addr_fmt == AF_CLASSIC:
            addr = sk.address(chain=chain)
        elif addr_fmt == AF_P2WPKH_P2SH:
            pkh = sk.hash160()
            digest = hash160(b'\x00\x14' + pkh)
            addr = encode_base58_checksum(bytes([196 if testnet else 5]) + digest)
        elif addr_fmt == AF_P2TR:
            from bech32 import encode
            tweked_xonly = taptweak(sk.sec()[1:])
            addr = encode("tb" if testnet else "bc", 1, tweked_xonly)
        else:
            pkh = sk.hash160()
            addr = bech32_encode('tb' if testnet else 'bc', 0, pkh)
    
        if subaccount:
            # need to hint we're doing a non-zero acccount number
            goto_home()
            settings_set('axskip', True)
            pick_menu_item('Address Explorer')
            pick_menu_item('Account Number')
            enter_number(subaccount)
            pick_menu_item(menu_item)
            press_cancel()

    cmd = (f'from ownership import OWNERSHIP;'
           f'c,w,path=OWNERSHIP.search({addr!r});'
           f'RV.write(repr([c, w.name, path]))')

    if not from_empty:
        # we expect here to find address from cache
        # so we first need to generate proper cache
        lst = sim_exec(cmd, timeout=None)
        assert 'Traceback' not in lst, lst
        lst = eval(lst)
        assert len(lst) == 3
        assert lst[0] is False  # not from cache, needed to build it


    lst = sim_exec(cmd, timeout=None)
    assert 'Traceback' not in lst, lst
    lst = eval(lst)
    assert len(lst) == 3

    from_cache, got_name, got_path = lst

    assert from_cache == (not from_empty)
    if is_q1:
        assert expect_name in got_name
    else:
        assert expect_name.split(" ")[-1] in got_name
    if subaccount:
        # not expected for multisig, since we have proper wallet name
        if is_q1:
            assert f'Account#{subaccount}' in got_name
        else:
            assert f'Acct#{subaccount}' in got_name

    assert got_path == (change_idx, offset)

@pytest.mark.parametrize('valid', [ True, False] )
@pytest.mark.parametrize('netcode', [ "BTC", "XTN"] )
@pytest.mark.parametrize('method', [ 'qr', 'nfc'] )
@pytest.mark.parametrize('multisig', [ True, False] )
def test_ux(valid, netcode, method,
    sim_exec, wipe_cache, use_testnet, goto_home, pick_menu_item,
    press_cancel, press_select, settings_set, is_q1, nfc_write, need_keypress,
    cap_screen, cap_story, load_shared_mod, scan_a_qr, skip_if_useless_way,
    sign_msg_from_address, multisig, import_ms_wallet, clear_miniscript, verify_qr_address,
    src_root_dir, sim_root_dir
):
    skip_if_useless_way(method)
    addr_fmt = AF_CLASSIC

    testnet = (netcode == "XTN")

    if valid:
        if multisig:
            from test_multisig import make_ms_address, HARD
            M, N = 2, 3

            expect_name = f'own_ux_test'
            clear_miniscript()
            keys = import_ms_wallet(M, N, "p2wsh", name=expect_name, accept=1)

            # iffy: no cosigner index in this wallet, so indicated that w/ path_mapper
            addr, scriptPubKey, script, details = make_ms_address(
                M, keys, is_change=0, idx=50, addr_fmt=AF_P2WSH,
                testnet=int(testnet), path_mapper=lambda cosigner: [HARD(45), 0, 50]
            )
            addr_fmt = AF_P2WSH
        else:
            mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv)
            path = "m/44h/{ct}h/{acc}h/0/3".format(acc=0, ct=(1 if testnet else 0))
            sk = mk.subkey_for_path(path)
            addr = sk.address(chain=netcode)
    else:
        addr = fake_address(addr_fmt, testnet)

    if method == 'qr':
        goto_home()
        pick_menu_item('Scan Any QR Code')
        scan_a_qr(addr)
        time.sleep(1)

        title, story = cap_story()

        assert addr == addr_from_display_format(story.split("\n\n")[0])
        assert '(1) to verify ownership' in story
        need_keypress('1')

    elif method == 'nfc':
        
        cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py')
        n = cc_ndef.ndefMaker()
        n.add_text(addr)
        ccfile = n.bytes()

        # run simulator w/ --set nfc=1 --eff
        goto_home()
        pick_menu_item('Advanced/Tools')
        pick_menu_item('NFC Tools')
        pick_menu_item('Verify Address')
        with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f:
            f.write(ccfile)
        nfc_write(ccfile)
        #press_select()

    else:
        raise ValueError(method)

    time.sleep(1)
    title, story = cap_story()
    assert addr == addr_from_display_format(story.split("\n\n")[0])

    if title == 'Unknown Address' and not testnet:
        assert 'That address is not valid on Bitcoin Testnet' in story
    elif valid:
        assert title == ('Verified Address' if is_q1 else "Verified!")
        assert 'Found in wallet' in story
        assert 'Derivation path' in story

        if is_q1:
            # check it can display as QR from here
            need_keypress(KEY_QR)
            verify_qr_address(addr_fmt, addr)
            press_cancel()

        if multisig:
            assert expect_name in story
            assert "Press (0) to sign message with this key" not in story
        else:
            assert 'P2PKH' in story
            assert "Press (0) to sign message with this key" in story
            need_keypress('0')
            msg = "coinkite CC the most solid HWW"
            sign_msg_from_address(msg, addr, path, addr_fmt, method, netcode)

    else:
        assert title == 'Unknown Address'
        assert 'Searched 1528' in story  # max
        assert "1 wallet(s)" in story
        assert 'without finding a match' in story

@pytest.mark.parametrize("af", ["P2SH-Segwit", "Segwit P2WPKH", "Classic P2PKH", "Taproot P2TR", "ms0", "msc0", "msc2"])
def test_address_explorer_saver(af, wipe_cache, settings_set, goto_address_explorer,
                                pick_menu_item, need_keypress, sim_exec, clear_miniscript,
                                import_ms_wallet, press_select, goto_home, nfc_write,
                                load_shared_mod, load_export_and_verify_signature,
                                cap_story, load_export, offer_minsc_import, is_q1,
                                src_root_dir, sim_root_dir):
    goto_home()
    wipe_cache()
    settings_set('accts', [])

    if af == "ms0":
        clear_miniscript()
        import_ms_wallet(2, 3, name=af)
        press_select()  # accept ms import
    elif "msc" in af:
        from test_miniscript import CHANGE_BASED_DESCS
        which = int(af[-1])
        title, story = offer_minsc_import(json.dumps({"name": af, "desc": CHANGE_BASED_DESCS[which]}))
        assert "Create new miniscript wallet?" in story
        press_select()  # accept

    goto_address_explorer()
    pick_menu_item(af)
    need_keypress("1")  # save to SD

    cmd = f'import os; RV.write(repr([i for i in os.listdir() if ".own" in i]))'
    lst = sim_exec(cmd)
    assert 'Traceback' not in lst, lst
    lst = eval(lst)
    assert lst

    title, body = cap_story()
    contents, _, _ = load_export_and_verify_signature(body, "sd", label="Address summary")

    addr_dump = io.StringIO(contents)
    cc = csv.reader(addr_dump)
    hdr = next(cc)
    addr = None
    assert hdr[:2] == ['Index', 'Payment Address']
    for n, (idx, addr, *_) in enumerate(cc, start=0):
        assert int(idx) == n
        if idx == 200:
            addr = addr

    cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py')
    n = cc_ndef.ndefMaker()
    n.add_text(addr)
    ccfile = n.bytes()

    # run simulator w/ --set nfc=1 --eff
    goto_home()
    pick_menu_item('Advanced/Tools')
    pick_menu_item('NFC Tools')
    pick_menu_item('Verify Address')
    with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f:
        f.write(ccfile)

    nfc_write(ccfile)

    time.sleep(1)
    title, story = cap_story()

    assert addr == addr_from_display_format(story.split("\n\n")[0])
    assert title == ('Verified Address' if is_q1 else "Verified!")
    assert 'Found in wallet' in story
    assert 'Derivation path' in story
    if is_q1:
        assert af in story
    else:
        if "ms" in af:
            assert af in story
        elif af == "P2SH-Segwit":
            assert af in story
        else:
            part = af.split(" ")[1]
            assert part in story


def test_ae_saver(wipe_cache, settings_set, goto_address_explorer, cap_story,
                  pick_menu_item, need_keypress, sim_exec, is_q1,
                  import_ms_wallet, press_select, goto_home, nfc_write,
                  load_shared_mod, load_export_and_verify_signature,
                  set_addr_exp_start_idx, use_testnet):

    cmd = lambda a: (
        f'from ownership import OWNERSHIP;'
        f'c,w,path=OWNERSHIP.search({a!r});'
        f'RV.write(repr([c, w.name, path]))')

    def cache_check(a, from_cache):
        l = sim_exec(cmd(a), timeout=None)
        assert 'Traceback' not in l, l
        assert eval(l)[0] == from_cache

    use_testnet()
    goto_home()
    wipe_cache()
    settings_set('accts', [])
    settings_set('aei', True)

    goto_address_explorer()
    set_addr_exp_start_idx(7)  # starting from index 7
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary")
    addr_dump = io.StringIO(contents)
    cc = csv.reader(addr_dump)
    hdr = next(cc)
    assert hdr == ['Index', 'Payment Address', 'Derivation']
    addrs = {}
    for idx, addr, deriv in cc:
        addrs[int(idx)] = addr

    # nothing was created from above as start index was 7
    cache_check(addrs[7], False)
    # now we have cached addresses up to 27
    for i in range(8, 28):
        cache_check(addrs[i], True)

    # cache file position at 27 (aka count)
    goto_address_explorer()
    set_addr_exp_start_idx(1)  # starting from index 1
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    load_export_and_verify_signature(body, "sd", label="Address summary")

    # after above we must have first 250 addresses cached
    cache_check(addrs[249], True)

    # cache file position at 250 (aka count)
    goto_address_explorer()
    set_addr_exp_start_idx(250)  # starting from index 250
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary")
    addr_dump = io.StringIO(contents)
    cc = csv.reader(addr_dump)
    hdr = next(cc)
    assert hdr == ['Index', 'Payment Address', 'Derivation']
    addrs = {}
    for idx, addr, deriv in cc:
        addrs[int(idx)] = addr

    # after above we must have first 500 addresses cached
    cache_check(addrs[300], True)
    cache_check(addrs[400], True)
    cache_check(addrs[499], True)

    # now addresses that we already have, does nothing
    goto_address_explorer()
    set_addr_exp_start_idx(100)  # starting from index 100
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    load_export_and_verify_signature(body, "sd", label="Address summary")
    cache_check(addrs[499], True)

    # now move count up via ownership
    mk = BIP32Node.from_wallet_key(simulator_fixed_tprv)
    sk = mk.subkey_for_path("84h/1h/0h/0/580")
    addr = bech32_encode('tb', 0, sk.hash160())
    cache_check(addr, False)
    # now count at 600 (580+20)

    # now over the max but with some we already have
    goto_address_explorer()
    set_addr_exp_start_idx(550)  # starting from index 550 (would go up to 800)
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    contents, sig_addr, _ = load_export_and_verify_signature(body, "sd", label="Address summary")
    addr_dump = io.StringIO(contents)
    cc = csv.reader(addr_dump)
    hdr = next(cc)
    assert hdr == ['Index', 'Payment Address', 'Derivation']
    addrs = {}
    for idx, addr, deriv in cc:
        addrs[int(idx)] = addr

    assert 799 in addrs
    cache_check(addrs[763], True)  # max

    # start idx over max stored addresses
    goto_address_explorer()
    set_addr_exp_start_idx(764)  # starting from index 764
    pick_menu_item("Segwit P2WPKH")
    need_keypress("1")  # save to SD

    time.sleep(.1)
    title, body = cap_story()
    load_export_and_verify_signature(body, "sd", label="Address summary")
    # does notthing harmful, nothing added

    cache_check(addrs[763], True)  # max
    l = sim_exec(cmd(addrs[764]), timeout=None)
    assert 'Traceback' in l
    assert 'Searched 1528' in l  # max
    assert "1 wallet(s)" in l
    assert 'without finding a match' in l


def test_regtest_addr_on_mainnet(goto_home, is_q1, pick_menu_item, scan_a_qr, nfc_write, cap_story,
                                 need_keypress, load_shared_mod, use_mainnet, src_root_dir, sim_root_dir):
    # testing bug in chains.possible_address_fmt
    # allowed regtest addresses to be allowed on main chain
    goto_home()
    use_mainnet()
    addr = "bcrt1qmff7njttlp6tqtj0nq7svcj2p9takyqm3mfl06"
    if is_q1:
        pick_menu_item('Scan Any QR Code')
        scan_a_qr(addr)
        time.sleep(1)

        title, story = cap_story()

        assert addr == addr_from_display_format(story.split("\n\n")[0])
        assert '(1) to verify ownership' in story
        need_keypress('1')

    else:
        cc_ndef = load_shared_mod('cc_ndef', f'{src_root_dir}/shared/ndef.py')
        n = cc_ndef.ndefMaker()
        n.add_text(addr)
        ccfile = n.bytes()

        # run simulator w/ --set nfc=1 --eff
        pick_menu_item('Advanced/Tools')
        pick_menu_item('NFC Tools')
        pick_menu_item('Verify Address')
        with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f:
            f.write(ccfile)
        nfc_write(ccfile)
        # press_select()

    time.sleep(1)
    title, story = cap_story()
    assert addr == addr_from_display_format(story.split("\n\n")[0])

    assert title == 'Unknown Address'
    assert "not valid on Bitcoin Mainnet" in story


def test_20_more_build_after_match(sim_exec, import_ms_wallet, clear_miniscript, wipe_cache,
                                   settings_set):
    from test_multisig import make_ms_address, HARD

    cmd = lambda a: (
        f'from ownership import OWNERSHIP;'
        f'c,w,path=OWNERSHIP.search({a!r});'
        f'RV.write(repr([c, w.name, path]))')

    # create multisig wallet
    M, N = 2, 3
    expect_name = 'test20more'
    clear_miniscript()
    keys = import_ms_wallet(M, N, name=expect_name, accept=True, addr_fmt="p2wsh")

    make_a = lambda index: make_ms_address(
        M, keys,
        is_change=False, idx=index, addr_fmt=AF_P2WSH, testnet=True,
        path_mapper=lambda cosigner: [HARD(45), 0, index])

    def cache_check(index, from_cache):
        a = make_a(index)[0]
        l = sim_exec(cmd(a), timeout=None)
        assert 'Traceback' not in l, l
        assert eval(l)[0] == from_cache

    # clean slate
    wipe_cache()
    settings_set('accts', [])

    # generate 10th (idx=9) address (external)
    # first run, generated first 10 addresses + 20
    cache_check(9, False)

    # now we can go up to index 29 - all must come from cache
    for i in range(10, 30):
        cache_check(i, True)

    # idx 30 - not in cache
    # but will cache next 20 addrs
    cache_check(30, False)

    # now we can go up to index 51 - all must come from cache
    for i in range(31, 51):
        cache_check(i, True)

    # idx 51 - not in cache
    # but will cache next 20 addrs
    cache_check(51, False)

    cache_check(760, False)
    cache_check(761, True)
    cache_check(762, True)
    cache_check(763, True)

    # after max - not gonna find
    addr = make_a(764)[0]
    l = sim_exec(cmd(addr), timeout=None)
    assert 'Traceback' in l
    assert 'Searched 1528' in l  # max
    assert "1 wallet(s)" in l
    assert 'without finding a match' in l


def test_named_wallet_search_fail(load_shared_mod, goto_home, pick_menu_item, nfc_write,
                                  cap_story, sim_root_dir):
    addr = fake_address(AF_P2WSH, True)
    addr = f"{addr}?wallet=unknown"
    cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py')
    n = cc_ndef.ndefMaker()
    n.add_text(addr)
    ccfile = n.bytes()

    # run simulator w/ --set nfc=1 --eff
    goto_home()
    pick_menu_item('Advanced/Tools')
    pick_menu_item('NFC Tools')
    pick_menu_item('Verify Address')
    with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f:
        f.write(ccfile)
    nfc_write(ccfile)

    time.sleep(1)
    title, story = cap_story()
    assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0])
    assert "Wallet 'unknown' not defined." in story


@pytest.mark.parametrize('valid', [True, False])
@pytest.mark.parametrize('method', ["qr", "nfc"])
@pytest.mark.parametrize('wname', ["msnm", "Longer Wallet Name"])
def test_named_wallet_search(wname, valid, method, clear_miniscript, import_ms_wallet, is_q1,
                             load_shared_mod, goto_home, pick_menu_item, scan_a_qr,
                             cap_story, need_keypress, nfc_write, use_testnet,
                             wipe_cache, settings_set, sim_root_dir):

    from test_multisig import make_ms_address, HARD

    if method == "qr" and (not is_q1):
        raise pytest.skip("QR Mk")

    wipe_cache()  # very different codepaths
    settings_set('accts', [])
    use_testnet()
    M, N = 2, 3
    clear_miniscript()
    ms_data = {}
    # all ms wallets have same address format, different M/N
    for i in range(3):
        idx = 5
        if i == 2:
            idx = 763
        name = f'{wname}{i}'
        keys = import_ms_wallet(M+i, N+i, "p2wsh", name=name, accept=True)
        # last address
        addr, scriptPubKey, script, details = make_ms_address(
            M+i, keys, is_change=0, idx=idx, addr_fmt=AF_P2WSH,
            testnet=True, path_mapper=lambda cosigner: [HARD(45), 0, idx]
        )
        ms_data[name] = (addr, scriptPubKey, script, keys)

    if valid:
        # msnw2 -> last added wallet
        addr, *_ = ms_data[f"{wname}{i}"]
    else:
        # will fail, even tho address is present in different wallet
        # with wallet=<wal> only specified wallet is searched
        addr, *_ = ms_data[f"{wname}0"]

    # will only search specified wallet
    addr = f"{addr}?wallet={wname}{i}".replace(' ', '%20')

    if method == 'qr':
        goto_home()
        pick_menu_item('Scan Any QR Code')
        scan_a_qr(addr)
        time.sleep(1)

        title, story = cap_story()

        assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0])
        assert '(1) to verify ownership' in story
        need_keypress('1')

    elif method == 'nfc':
        cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py')
        n = cc_ndef.ndefMaker()
        n.add_text(addr)
        ccfile = n.bytes()

        # run simulator w/ --set nfc=1 --eff
        goto_home()
        pick_menu_item('Advanced/Tools')
        pick_menu_item('NFC Tools')
        pick_menu_item('Verify Address')
        with open(f'{sim_root_dir}/debug/nfc-addr.ndef', 'wb') as f:
            f.write(ccfile)
        nfc_write(ccfile)
        # press_select()

    else:
        raise ValueError(method)

    time.sleep(1)
    title, story = cap_story()
    assert addr.split("?", 1)[0] == addr_from_display_format(story.split("\n\n")[0])

    if valid:
        assert title == ('Verified Address' if is_q1 else "Verified!")
        assert 'Found in wallet' in story
        assert 'Derivation path' in story
        assert f"{wname}" in story

    else:
        assert title == 'Unknown Address'
        assert 'Searched 1528' in story  # max
        assert "1 wallet(s)" in story
        assert 'without finding a match' in story

# EOF
