Skip to content

Commit 5877272

Browse files
author
kratm2
committed
make pi dummy address resistent, refactoring
1 parent 3076b38 commit 5877272

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

electrum/address_synchronizer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ def remove_from_spent_outpoints():
417417
scripthash = bitcoin.script_to_scripthash(txo.scriptpubkey)
418418
prevout = TxOutpoint(bfh(tx_hash), idx)
419419
self.db.remove_prevout_by_scripthash(scripthash, prevout=prevout, value=txo.value)
420-
self.db.remove_silent_payment_address(txo.address)
420+
if addr := txo.address:
421+
self.db.remove_silent_payment_address(addr)
421422
util.trigger_callback('adb_removed_tx', self, tx_hash, tx)
422423

423424
def get_depending_transactions(self, tx_hash: str) -> Set[str]:

electrum/payment_identifier.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -278,20 +278,23 @@ def parse(self, text: str):
278278
# no address, no bolt11 and no silent payment address, invalid
279279
self.set_state(PaymentIdentifierState.INVALID)
280280
return
281-
elif self.bip21.get('address') == DummyAddress.SILENT_PAYMENT:
281+
elif DummyAddress.is_dummy_address(self.bip21.get('address')):
282282
self.set_state(PaymentIdentifierState.INVALID)
283-
return # user manually entered DummyAddress.SILENT_PAYMENT
283+
return # user manually entered DummyAddress
284284
self.set_state(PaymentIdentifierState.AVAILABLE)
285285
elif self.parse_output(text)[0]:
286-
scriptpubkey, is_address = self.parse_output(text)
287-
if bitcoin.script_to_address(scriptpubkey) == DummyAddress.SILENT_PAYMENT:
288-
sp_address = self.parse_address(text)
289-
if sp_address == DummyAddress.SILENT_PAYMENT:
286+
scriptpubkey, is_address, sp_addr = self.parse_output(text)
287+
addr = bitcoin.script_to_address(scriptpubkey) if is_address else None
288+
if addr == DummyAddress.SILENT_PAYMENT:
289+
if sp_addr is None:
290290
self.set_state(PaymentIdentifierState.INVALID) # user manually entered DummyAddress.SILENT_PAYMENT
291291
return
292-
self.sp_address = sp_address
292+
self.sp_address = sp_addr
293293
self._type = PaymentIdentifierType.SILENT_PAYMENT
294294
else:
295+
if DummyAddress.is_dummy_address(addr):
296+
self.set_state(PaymentIdentifierState.INVALID) # user manually entered dummy lighting address
297+
return
295298
self._type = PaymentIdentifierType.SPK
296299
self.spk = scriptpubkey
297300
self.spk_is_address = is_address
@@ -500,7 +503,7 @@ def get_onchain_outputs(self, amount, allow_silent_payment=True):
500503
else:
501504
address = sp_address # use silent payment address
502505

503-
scriptpubkey, is_address = self.parse_output(address)
506+
scriptpubkey, is_address, _ = self.parse_output(address)
504507
assert is_address # unlikely, but make sure it is an address, not a script
505508
output = PartialTxOutput(scriptpubkey=scriptpubkey, value=amount)
506509
if output.address == DummyAddress.SILENT_PAYMENT:
@@ -543,30 +546,34 @@ def parse_address_and_amount(self, line: str) -> PartialTxOutput:
543546
x, y = line.split(',')
544547
except ValueError:
545548
raise Exception("expected two comma-separated values: (address, amount)") from None
546-
scriptpubkey, is_address = self.parse_output(x)
549+
scriptpubkey, is_address, sp_addr = self.parse_output(x)
547550
if not scriptpubkey:
548551
raise Exception('Invalid address')
549552
amount = self.parse_amount(y)
550553
output = PartialTxOutput(scriptpubkey=scriptpubkey, value=amount)
551554
if output.address == DummyAddress.SILENT_PAYMENT:
552-
output.sp_addr = SilentPaymentAddress(self.parse_address(x)) # maybe parsing again isn't needed
555+
output.sp_addr = SilentPaymentAddress(sp_addr) # raises if user entered manually silent-dummy-addr -> invalid
556+
elif DummyAddress.is_dummy_address(output.address):
557+
raise Exception('user manually entered dummy address')
553558
return output
554559

555-
def parse_output(self, x: str) -> Tuple[Optional[bytes], bool]:
560+
def parse_output(self, x: str) -> Tuple[Optional[bytes], bool, Optional[str]]:
556561
try:
557562
address = self.parse_address(x)
563+
sp_addr = None
558564
if is_silent_payment_address(address):
565+
sp_addr = address
559566
address = DummyAddress.SILENT_PAYMENT
560-
return bitcoin.address_to_script(address), True
567+
return bitcoin.address_to_script(address), True, sp_addr
561568
except Exception as e:
562569
pass
563570
try:
564571
m = re.match('^' + RE_SCRIPT_FN + '$', x)
565572
script = self.parse_script(str(m.group(1)))
566-
return script, False
573+
return script, False, None
567574
except Exception as e:
568575
pass
569-
return None, False
576+
return None, False, None
570577

571578
def parse_script(self, x: str) -> bytes:
572579
script = bytearray()

tests/test_payment_identifier.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from electrum.bip21 import MissingFallbackAddress
55
from electrum.invoices import Invoice
66
from electrum.payment_identifier import (maybe_extract_lightning_payment_identifier, PaymentIdentifier,
7-
PaymentIdentifierType, invoice_from_payment_identifier)
7+
PaymentIdentifierType, invoice_from_payment_identifier, PaymentIdentifierState)
88
from electrum.wallet import restore_wallet_from_text
99

1010
from . import ElectrumTestCase
1111
from electrum.transaction import PartialTxOutput
12-
from electrum.bitcoin import script_to_address, DummyAddress
12+
from electrum.bitcoin import DummyAddress
1313

1414

1515
class WalletMock:
@@ -403,3 +403,15 @@ def test_silent_payment_bip21(self):
403403
pi.get_onchain_outputs(0, allow_silent_payment=False)[0].address,
404404
'1RustyRX2oai4EYYDpQGWvEL62BBGqN9T'
405405
)
406+
407+
def test_dummy_address_abuse(self):
408+
for dummy_addr in [DummyAddress.SILENT_PAYMENT, DummyAddress.SWAP, DummyAddress.CHANNEL]:
409+
for pi_str in [
410+
dummy_addr, # spk/silent payment
411+
f"1RustyRX2oai4EYYDpQGWvEL62BBGqN9T,2\n{dummy_addr}, 5", # multiline
412+
f"bitcoin:{dummy_addr}", # bip21
413+
]:
414+
pi = PaymentIdentifier(self.wallet, pi_str)
415+
self.assertEqual(pi.state, PaymentIdentifierState.INVALID)
416+
417+

0 commit comments

Comments
 (0)