slight rework

This commit is contained in:
Peter D. Gray 2026-02-25 11:05:24 -05:00
parent d53c7b2e1b
commit 6f366d1603
No known key found for this signature in database
GPG Key ID: A2DCD558C2BE5D7C
3 changed files with 25 additions and 35 deletions

View File

@ -5,7 +5,6 @@
import stash, gc, history, sys, ngu, ckcc, chains
from ustruct import unpack_from, unpack, pack
from ubinascii import hexlify as b2a_hex
from ucollections import OrderedDict
from utils import xfp2str, B2A, keypath_to_str
from utils import seconds2human_readable, datetime_from_timestamp, datetime_to_str
from chains import NLOCK_IS_TIME
@ -1762,20 +1761,16 @@ class psbtObject(psbtProxy):
foreign = []
total_in = 0
prevouts_max_len = 50
prevouts = OrderedDict()
prevouts = set()
for i, txi in self.input_iter():
# check for duplicate inputs
if len(prevouts) >= prevouts_max_len:
first = next(iter(prevouts)) # O(1) in mpy
del prevouts[first]
k = (txi.prevout.hash, txi.prevout.n)
if k in prevouts:
raise FatalPSBTIssue("Duplicate inputs")
k = txi.prevout.n, txi.prevout.hash
if k in prevouts: # O(1)
raise FatalPSBTIssue("Duplicate inputs!")
prevouts[k] = True
if len(prevouts) < 100:
prevouts.add(k)
inp = self.inputs[i]
if inp.fully_signed:

View File

@ -3647,16 +3647,14 @@ def test_txn_nVersion_zero(segwit, fake_txn, start_sign, cap_story, goto_home):
assert "txn version" in story
@pytest.mark.parametrize("segwit_in", [True, False])
@pytest.mark.parametrize("num_ins", [2, 50, 51])
@pytest.mark.parametrize("num_ins", [2, 110])
def test_duplicate_inputs(segwit_in, num_ins, fake_txn, start_sign, end_sign, cap_story):
psbt = fake_txn(num_ins, 2, segwit_in=segwit_in, dupe_ins=[num_ins-1])
start_sign(psbt)
title, story = cap_story()
if num_ins <= 50:
# only works if duplicate is no longer than 50 inputs from original
assert "Duplicate inputs!" in story
if num_ins <= 100:
assert "Duplicate input" in story
else:
# does not work
assert title == "OK TO SEND?"
# EOF

View File

@ -27,10 +27,6 @@ def fake_txn(dev, pytestconfig):
psbt_v2=None, input_amount=1E8, unknown_out_script=None, lock_time=0,
sequences=None, sighashes=None, dupe_ins=[]):
# dupe_ins cannot contain zero, as that will be the duplicated input
if dupe_ins:
assert 0 not in dupe_ins
psbt = BasicPSBT()
if psbt_v2 is None:
@ -62,17 +58,12 @@ def fake_txn(dev, pytestconfig):
# - each input is 1BTC
# addr where the fake money will be stored.
if i in dupe_ins:
# always duplicate zeroth input
subkey = mk.subkey_for_path(subpath % 0)
else:
subkey = mk.subkey_for_path(subpath % i)
subkey = mk.subkey_for_path(subpath % i)
sec = subkey.sec()
assert len(sec) == 33, "expect compressed"
assert subpath[0:2] == '0/'
idx = 0 if i in dupe_ins else i
psbt.inputs[i].bip32_paths[sec] = xfp + struct.pack('<II', 0, idx)
psbt.inputs[i].bip32_paths[sec] = xfp + struct.pack('<II', 0, i)
# UTXO that provides the funding for to-be-signed txn
supply = CTransaction()
@ -102,7 +93,7 @@ def fake_txn(dev, pytestconfig):
if segwit_in:
# just utxo for segwit
psbt.inputs[i].witness_utxo = supply.vout[0 if i in dupe_ins else -1].serialize()
psbt.inputs[i].witness_utxo = supply.vout[-1].serialize()
else:
# whole tx for pre-segwit
psbt.inputs[i].utxo = supply.serialize_with_witness()
@ -131,14 +122,20 @@ def fake_txn(dev, pytestconfig):
seq = sequences[0]
spendable = CTxIn(COutPoint(supply.sha256, 0), nSequence=seq)
txn.vin.append(spendable)
if i not in dupe_ins:
txn.vin.append(spendable)
if psbt_v2:
psbt.inputs[i].previous_txid = supply.hash
psbt.inputs[i].prevout_idx = 0
psbt.inputs[i].sequence = seq
# psbt.inputs[i].req_time_locktime = None
# psbt.inputs[i].req_height_locktime = None
if psbt_v2:
psbt.inputs[i].previous_txid = supply.hash
psbt.inputs[i].prevout_idx = 0
psbt.inputs[i].sequence = seq
# psbt.inputs[i].req_time_locktime = None
# psbt.inputs[i].req_height_locktime = None
else:
assert i != 0, 'cant dup first input'
txn.vin.append(txn.vin[-1])
from copy import deepcopy
psbt.inputs[i] = deepcopy(psbt.inputs[i-1])
for i in range(num_outs):
# random P2PKH