bugfix: 1of1 multisig

This commit is contained in:
scgbckbone 2026-05-22 17:46:34 +02:00 committed by doc-hex
parent 6716fcbacb
commit 6fd256dbdc
4 changed files with 45 additions and 20 deletions

View File

@ -41,6 +41,7 @@ This lists the new changes that have not yet been published in a normal release.
- Bugfix: Handle malformed NDEF records robustly. Thanks, @Damir
- Bugfix: Ignore `bkpw` if added to backup. Thanks [@dmonakhov](https://github.com/dmonakhov)
- Bugfix: Keep NFC export tag live for repeated probes
- Bugfix: Fix 1of1 multisig signing failure
# Mk Specific Changes

View File

@ -829,17 +829,20 @@ class psbtInputProxy(psbtProxy):
self.scriptSig = redeem_script
# new cheat: psbt creator probably telling us exactly what key
# to use, by providing exactly one. This is ideal for p2sh wrapped p2pkh
if len(subpaths) == 1:
if not addr_is_segwit and len(redeem_script) == 22 and \
redeem_script[0] == 0 and redeem_script[1] == 20:
# segwit p2pkh wrapped in p2sh: exactly one key, not multisig.
# psbt creator tells us the key by providing exactly one subpath.
self.addr_fmt = AF_P2WPKH_P2SH
addr = redeem_script[2:22]
self.is_segwit = True
assert len(subpaths) == 1, "p2sh-p2wpkh needs one key"
which_key, = subpaths.keys()
else:
# Assume we'll be signing with any key we know
# - limitation: we cannot be two legs of a multisig (only if CCC feature used)
# - but if partial sig already in place, ignore that one
if not which_key:
which_key = set()
self.is_multisig = True
which_key = set()
for pubkey, path in subpaths.items():
if self.part_sigs and (pubkey in self.part_sigs):
# pubkey has already signed, so ignore
@ -850,20 +853,8 @@ class psbtInputProxy(psbtProxy):
which_key.add(pubkey)
elif pubkey in psbt.wif_store:
# maybe sset some input value
which_key.add(pubkey)
if not addr_is_segwit and \
len(redeem_script) == 22 and \
redeem_script[0] == 0 and redeem_script[1] == 20:
# it's actually segwit p2pkh inside p2sh
self.addr_fmt = AF_P2WPKH_P2SH
addr = redeem_script[2:22]
self.is_segwit = True
else:
# multiple keys involved
self.is_multisig = True
if self.witness_script and not self.is_segwit and self.is_multisig:
# bugfix
self.addr_fmt = AF_P2WSH_P2SH

View File

@ -1510,6 +1510,24 @@ def test_ms_sign_simple(M_N, num_ins, dev, addr_fmt, clear_ms, incl_xpubs, impor
else:
try_sign(psbt)
@pytest.mark.parametrize("finalize", [True, False])
def test_1of1_multisig_sign(finalize, clear_ms, import_ms_wallet, fake_ms_txn, start_sign,
end_sign, cap_story):
# Minimal 1-of-1 multisig: import the wallet, then sign a 1-in/1-out PSBT.
clear_ms()
M = N = 1
keys = import_ms_wallet(M, N, accept=True)
psbt = fake_ms_txn(1, 1, M, keys)
start_sign(psbt, finalize=finalize)
title, story = cap_story()
assert title == "OK TO SEND?"
end_sign(accept=True, finalize=finalize)
@pytest.mark.unfinalized
@pytest.mark.bitcoind
@pytest.mark.parametrize('num_ins', [ 15 ])

View File

@ -1664,6 +1664,21 @@ def test_wrong_pubkey(dev, try_sign, fake_txn):
msg = ee.value.args[0]
assert ('pubkey vs. address wrong' in msg)
def test_p2sh_p2wpkh_multiple_bip32_paths_rejected(fake_txn, start_sign, cap_story):
psbt = fake_txn(1, 1, segwit_in=True, wrapped=True)
po = BasicPSBT().parse(psbt)
other = BIP32Node.from_master_secret(os.urandom(32))
other_key = other.subkey_for_path("0/0")
po.inputs[0].bip32_paths[other_key.sec()] = other.fingerprint() + struct.pack('<II', 0, 0)
start_sign(po.as_bytes(), finalize=True)
title, story = cap_story()
assert title == "Failure"
assert "p2sh-p2wpkh needs one key" in story
def test_incomplete_signing(dev, try_sign, fake_txn, cap_story):
# psbt where we only sign one input
# - must not allow finalization