Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions electrum/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,10 @@ class BaseDecodeError(BitcoinException): pass


def base_encode(v: bytes, *, base: int) -> str:
""" encode v, which is a string of bytes, to base58."""
""" encode v, which is a string of bytes, to base58.

note: time complexity is O(len(v)^2), due to big-int arithmetic.
"""
assert_bytes(v)
if base not in (58, 43):
raise ValueError('not supported base: {}'.format(base))
Expand All @@ -552,10 +555,11 @@ def base_encode(v: bytes, *, base: int) -> str:
newlen = len(v)

num = int.from_bytes(v, byteorder='big')
string = b""
string_rev = bytearray()
while num:
num, idx = divmod(num, base)
string = chars[idx:idx + 1] + string
string_rev += chars[idx:idx + 1]
string = string_rev[::-1]

result = chars[0:1] * (origlen - newlen) + string
return result.decode('ascii')
Expand All @@ -564,6 +568,8 @@ def base_encode(v: bytes, *, base: int) -> str:
def base_decode(v: Union[bytes, str], *, base: int) -> Optional[bytes]:
""" decode v into a string of len bytes.

note: time complexity is O(len(v)^2), due to big-int arithmetic.

based on the work of David Keijser in https://github.com/keis/base58
"""
# assert_bytes(v)
Expand Down
22 changes: 16 additions & 6 deletions electrum/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,8 @@ def to_qr_data(self) -> Tuple[str, bool]:
tx.convert_all_utxos_to_witness_utxos()
is_complete = False
tx_bytes = tx.serialize_as_bytes()
return base_encode(tx_bytes, base=43), is_complete
tx_base43 = base_encode(tx_bytes, base=43) # FIXME this takes quadratic time in len(tx)
return tx_base43, is_complete

def txid(self) -> Optional[str]:
if self._cached_txid is None:
Expand Down Expand Up @@ -1499,17 +1500,26 @@ def convert_raw_tx_to_hex(raw: Union[str, bytes]) -> str:
return binascii.unhexlify(raw).hex()
except Exception:
pass
# try base43
try:
return base_decode(raw, base=43).hex()
except Exception:
pass
# try base64
if raw[0:6] in ('cHNidP', b'cHNidP'): # base64 psbt
try:
return base64.b64decode(raw, validate=True).hex()
except Exception:
pass
# try base43
try:
# FIXME This takes quadratic time in len(tx).
# We could prefix all txs we base43-serialize with e.g. "BASE43TX:",
# (and break-compat with old versions). Then at least we would not attempt
# the expensive deser here if it's not needed.
if len(raw) > 30_000:
# note: base_decode for this length takes around 0.2 sec on my laptop.
# note: We only use/expect base43 inside QR codes. The max data a QR can fit is around 4 KB,
# serializing that to b43 results in a length of ~5500. 30k is already over 5x that.
raise ValueError("raw tx too large for base43")
return base_decode(raw, base=43).hex()
except Exception:
pass
# raw bytes
if isinstance(raw, (bytes, bytearray)):
return raw.hex()
Expand Down