From 88bd45b02e1e5dd9184c469107ebe9f7e2db9908 Mon Sep 17 00:00:00 2001 From: Kristaps Kaupe Date: Thu, 20 Jul 2023 01:57:52 +0300 Subject: [PATCH] Parse URI params in guaranteed order, for duplicates, last one wins --- jmbitcoin/jmbitcoin/bip21.py | 13 ++++++------- jmbitcoin/test/test_bip21.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/jmbitcoin/jmbitcoin/bip21.py b/jmbitcoin/jmbitcoin/bip21.py index 5aea31b..e92cbaf 100644 --- a/jmbitcoin/jmbitcoin/bip21.py +++ b/jmbitcoin/jmbitcoin/bip21.py @@ -5,7 +5,7 @@ from jmbitcoin import amount_to_sat from typing import Dict, List, Tuple, Union -from urllib.parse import parse_qs, quote, unquote_plus, urlencode, urlparse +from urllib.parse import parse_qsl, quote, unquote_plus, urlencode, urlparse import re @@ -29,18 +29,17 @@ def decode_bip21_uri(uri: str) -> Dict[str, Union[str, int]]: result = {} parsed = urlparse(uri) result['address'] = parsed.path - params = parse_qs(parsed.query) - for key in params: + params = parse_qsl(parsed.query) + for key, value in params: if key.startswith('req-'): raise ValueError("Unknown required parameter " + key + " in BIP21 URI.") if key == 'amount': - amount_str = params['amount'][0] - _validate_bip21_amount(amount_str) + _validate_bip21_amount(value) # Convert amount to sats, as used internally by JM - result['amount'] = amount_to_sat(amount_str + "btc") + result['amount'] = amount_to_sat(value + "btc") else: - result[key] = unquote_plus(params[key][0]) + result[key] = unquote_plus(value) return result diff --git a/jmbitcoin/test/test_bip21.py b/jmbitcoin/test/test_bip21.py index 87027ca..b902741 100644 --- a/jmbitcoin/test/test_bip21.py +++ b/jmbitcoin/test/test_bip21.py @@ -75,6 +75,17 @@ def test_bip21_decode(): assert(parsed['somethingyoudontunderstand'] == '50') assert(parsed['somethingelseyoudontget'] == '999') + # Test multiple amount parameters, last value should win. + parsed = btc.decode_bip21_uri( + 'bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=20.3&amount=50&label=Luke-Jr') + assert(parsed['address'] == '175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W') + assert(parsed['amount'] == 5000000000) + assert(parsed['label'] == 'Luke-Jr') + # Here are two amount parameters, first valid, second not valid, so URI is not valid. + with pytest.raises(ValueError): + btc.decode_bip21_uri( + 'bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=20.3&amount=100,000&label=Luke-Jr') + def test_bip21_encode(): assert(