better matching
This commit is contained in:
parent
789b87c33c
commit
f8d32a8272
@ -37,7 +37,8 @@ from public_constants import (
|
||||
PSBT_GLOBAL_TX_MODIFIABLE, PSBT_GLOBAL_OUTPUT_COUNT, PSBT_GLOBAL_INPUT_COUNT,
|
||||
PSBT_GLOBAL_FALLBACK_LOCKTIME, PSBT_GLOBAL_TX_VERSION, PSBT_IN_PREVIOUS_TXID,
|
||||
PSBT_IN_OUTPUT_INDEX, PSBT_IN_SEQUENCE, PSBT_IN_REQUIRED_TIME_LOCKTIME,
|
||||
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, MAX_PATH_DEPTH, MAX_SIGNERS
|
||||
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, MAX_SIGNERS,
|
||||
AF_P2WSH, AF_P2WSH_P2SH, AF_P2SH, AF_P2TR
|
||||
)
|
||||
|
||||
psbt_tmp256 = bytearray(256)
|
||||
@ -866,7 +867,7 @@ class psbtInputProxy(psbtProxy):
|
||||
self.is_p2sh = False
|
||||
which_key = None
|
||||
|
||||
addr_type, addr_or_pubkey, addr_is_segwit = utxo.get_address()
|
||||
addr_type, addr_or_pubkey, self.is_segwit = utxo.get_address()
|
||||
if addr_type == "op_return":
|
||||
self.required_key = None
|
||||
return
|
||||
@ -876,12 +877,12 @@ class psbtInputProxy(psbtProxy):
|
||||
# enough to allow the user to authorize the spend, so fail hard.
|
||||
raise FatalPSBTIssue('Unhandled scriptPubKey: ' + b2a_hex(addr_or_pubkey).decode())
|
||||
|
||||
if addr_is_segwit and not self.is_segwit:
|
||||
self.is_segwit = True
|
||||
|
||||
if addr_type == 'p2sh':
|
||||
# miniscript input
|
||||
self.is_p2sh = True
|
||||
if self.is_segwit:
|
||||
# we know this just from scriptPubKey --> utxo.get_address()
|
||||
addr_type = "p2wsh"
|
||||
|
||||
# we must have the redeem script already (else fail)
|
||||
ks = self.witness_script or self.redeem_script
|
||||
@ -910,7 +911,7 @@ class psbtInputProxy(psbtProxy):
|
||||
# slight chance of dup xfps, so handle
|
||||
which_key.add(pubkey)
|
||||
|
||||
if not addr_is_segwit and \
|
||||
if not self.is_segwit and \
|
||||
len(redeem_script) == 22 and \
|
||||
redeem_script[0] == 0 and redeem_script[1] == 20:
|
||||
# it's actually segwit p2pkh inside p2sh
|
||||
@ -986,13 +987,11 @@ class psbtInputProxy(psbtProxy):
|
||||
# pubkey provided is just wrong vs. UTXO
|
||||
raise FatalPSBTIssue('Input #%d: pubkey wrong' % my_idx)
|
||||
|
||||
else:
|
||||
# we don't know how to "solve" this type of input
|
||||
pass
|
||||
|
||||
if self.is_miniscript:
|
||||
try:
|
||||
xfp_paths = [item[1:] for item in self.taproot_subpaths.values() if len(item[1:]) > 1]
|
||||
xfp_paths = [item[1:]
|
||||
for item in self.taproot_subpaths.values()
|
||||
if len(item[1:]) > 1]
|
||||
except AttributeError:
|
||||
xfp_paths = list(self.subpaths.values())
|
||||
|
||||
@ -1000,13 +999,18 @@ class psbtInputProxy(psbtProxy):
|
||||
if psbt.active_miniscript:
|
||||
psbt.active_miniscript.matching_subpaths(xfp_paths), "wrong wallet"
|
||||
else:
|
||||
wal = MiniScriptWallet.find_match(xfp_paths)
|
||||
# if we do have actual script at hand, guess M/N for better matching
|
||||
# basic multisig matching
|
||||
M, N = disassemble_multisig_mn(self.scriptSig) if self.scriptSig else (None, None)
|
||||
af = {"p2wsh": AF_P2WSH, "p2sh-p2wsh": AF_P2WSH_P2SH,
|
||||
"p2sh": AF_P2SH, "p2tr": AF_P2TR}[addr_type]
|
||||
wal = MiniScriptWallet.find_match(xfp_paths, af, M, N)
|
||||
if not wal:
|
||||
raise FatalPSBTIssue('Unknown miniscript wallet')
|
||||
psbt.active_miniscript = wal
|
||||
|
||||
try:
|
||||
# contains PSBT merkle root verification
|
||||
# contains PSBT merkle root verification (if taproot)
|
||||
psbt.active_miniscript.validate_script_pubkey(utxo.scriptPubKey,
|
||||
xfp_paths, merkle_root)
|
||||
except BaseException as e:
|
||||
|
||||
@ -267,20 +267,23 @@ class MiniScriptWallet(WalletABC):
|
||||
return chains.current_chain()
|
||||
|
||||
@classmethod
|
||||
def find_match(cls, xfp_paths, addr_fmt=None, M_N=None):
|
||||
def find_match(cls, xfp_paths, addr_fmt=None, M=None, N=None):
|
||||
for rv in cls.iter_wallets():
|
||||
if addr_fmt is not None:
|
||||
if rv.addr_fmt != addr_fmt:
|
||||
continue
|
||||
|
||||
if M_N:
|
||||
if M and N:
|
||||
if not rv.m_n:
|
||||
continue
|
||||
if rv.m_n != M_N:
|
||||
|
||||
m, n = rv.m_n
|
||||
if m != M or n != N:
|
||||
continue
|
||||
|
||||
if rv.matching_subpaths(xfp_paths):
|
||||
return rv
|
||||
|
||||
return None
|
||||
|
||||
def xfp_paths(self, skip_unspend_ik=False):
|
||||
@ -354,7 +357,8 @@ class MiniScriptWallet(WalletABC):
|
||||
s = "Wallet Name:\n %s\n\n" % self.name
|
||||
if self.m_n:
|
||||
# basic multisig
|
||||
s += "Policy: %d of %d\n\n" % self.m_n
|
||||
M, N = self.m_n
|
||||
s += "Policy: %d of %d\n\n" % (M, N)
|
||||
|
||||
s += chains.addr_fmt_label(self.addr_fmt)
|
||||
s += "\n\n" + self.desc_tmplt
|
||||
@ -487,10 +491,10 @@ class MiniScriptWallet(WalletABC):
|
||||
err += "\n\n"
|
||||
assert False, err
|
||||
|
||||
assert self.desc_tmplt != rv.desc_tmplt \
|
||||
and self.keys_info != rv.keys_info, ("This wallet is a duplicate "
|
||||
"of already saved wallet "
|
||||
"%s.\n\n" % rv.name)
|
||||
else:
|
||||
if self.desc_tmplt == rv.desc_tmplt and self.keys_info == rv.keys_info:
|
||||
raise AssertionError ("This wallet is a duplicate of already"
|
||||
" saved wallet %s.\n\n" % rv.name)
|
||||
|
||||
async def confirm_import(self):
|
||||
nope, yes = (KEY_CANCEL, KEY_ENTER) if version.has_qwerty else ("x", "y")
|
||||
|
||||
@ -1000,10 +1000,11 @@ def fake_ms_txn(pytestconfig):
|
||||
# make a fake txn to supply each of the inputs
|
||||
# - each input is 1BTC
|
||||
# addr where the fake money will be stored.
|
||||
addr, scriptPubKey, script, details = make_ms_address(M, keys, idx=i, bip67=bip67,
|
||||
violate_script_key_order=violate_script_key_order, path_mapper=path_mapper,
|
||||
addr_fmt=af,
|
||||
testnet=net)
|
||||
addr, scriptPubKey, script, details = make_ms_address(
|
||||
M, keys, idx=i, bip67=bip67,
|
||||
violate_script_key_order=violate_script_key_order,
|
||||
path_mapper=path_mapper, addr_fmt=af, testnet=net
|
||||
)
|
||||
|
||||
print(i, script.hex())
|
||||
# lots of supporting details needed for p2sh inputs
|
||||
|
||||
@ -427,19 +427,20 @@ def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, dev, clear_miniscr
|
||||
txid_from_export_prompt, sim_root_dir):
|
||||
|
||||
# IMPORTANT: won't work if you start simulator with --ms flag. Use no args
|
||||
all_out_styles = [af for af in unmap_addr_fmt.keys() if af != "p2tr"]
|
||||
num_outs = len(all_out_styles)
|
||||
num_outs = 4
|
||||
af = "p2wsh"
|
||||
|
||||
clear_miniscript()
|
||||
use_regtest()
|
||||
|
||||
# create a wallet, with 3 bip39 pw's
|
||||
keys, select_wallet = make_myself_wallet(M, do_import=True)
|
||||
keys, select_wallet = make_myself_wallet(M, do_import=True, addr_fmt=af)
|
||||
N = len(keys)
|
||||
assert M<=N
|
||||
|
||||
psbt = fake_ms_txn(15, num_outs, M, keys, segwit_in=True, incl_xpubs=False,
|
||||
outstyles=all_out_styles, change_outputs=list(range(1,num_outs)))
|
||||
psbt = fake_ms_txn(15, num_outs, M, keys, inp_addr_fmt=af, incl_xpubs=False,
|
||||
outstyles=["p2sh-p2wsh", af, af, af],
|
||||
change_outputs=list(range(1,num_outs)))
|
||||
|
||||
with open(f'{sim_root_dir}/debug/myself-before.psbt', 'wb') as f:
|
||||
f.write(psbt)
|
||||
@ -537,16 +538,14 @@ def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, dev, clear_miniscr
|
||||
def test_teleport_big_ms(make_myself_wallet, clear_miniscript, fake_ms_txn, try_sign, cap_story,
|
||||
need_keypress, cap_menu, pick_menu_item, grab_payload, rx_complete,
|
||||
press_select, ndef_parse_txn_psbt, set_master_key, goto_home, press_nfc,
|
||||
nfc_read, settings_get, settings_set, open_microsd, import_ms_wallet,
|
||||
press_cancel):
|
||||
nfc_read, open_microsd, import_ms_wallet, press_cancel):
|
||||
|
||||
# define lots of wallets and do teleport from SD disk
|
||||
|
||||
clear_miniscript()
|
||||
M, N = 2, 15
|
||||
for i in range(5):
|
||||
keys = import_ms_wallet(M, N, name=f'ms{i}-test', unique=(i*73), accept=True,
|
||||
descriptor=False, bip67=True)
|
||||
keys = import_ms_wallet(M, N, name=f'ms{i}-test', unique=(i*73), accept=True, bip67=True)
|
||||
|
||||
# just use last wallet
|
||||
psbt = fake_ms_txn(1, 1, M, keys)
|
||||
@ -596,14 +595,12 @@ def test_teleport_big_ms(make_myself_wallet, clear_miniscript, fake_ms_txn, try_
|
||||
# capture QR+pw to go there
|
||||
pw, data, qr_raw = grab_payload('E')
|
||||
|
||||
tmp_ms = settings_get('multisig')
|
||||
|
||||
# switch to that key, receive it
|
||||
node, = [n for x,n,_ in keys if x == target_xfp]
|
||||
set_master_key(node.hwif(as_private=True))
|
||||
|
||||
# copy over the one MS wallet this xfp was involved in
|
||||
settings_set('multisig', [tmp_ms[-1]])
|
||||
import_ms_wallet(M, N, name=f'www', keys=keys, accept=True, bip67=True)
|
||||
|
||||
# import and sign
|
||||
rx_complete(('E', qr_raw), pw, expect_xfp=simulator_fixed_xfp)
|
||||
@ -651,7 +648,7 @@ def test_teleport_real_ms(dev, fake_ms_txn):
|
||||
# match the default paths created by CC in airgapped MS wallet creation.
|
||||
return str_to_path(deriv)
|
||||
|
||||
psbt = fake_ms_txn(3, 2, M, keys, fee=10000, outvals=None, segwit_in=False,
|
||||
psbt = fake_ms_txn(3, 2, M, keys, fee=10000, outvals=None, inp_addr_fmt="p2sh",
|
||||
outstyles=['p2pkh'], change_outputs=[], incl_xpubs=False,
|
||||
hack_change_out=False, input_amount=1E8, path_mapper=p2wsh_mapper)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user