1
0

transaction: tx_from_any: rm all whitespaces from str, none from bytes

- whitespaces are safe to remove from strings, and is convenient if we do this for users
- bytes-like inputs should be left alone: individual bytes that look like whitespaces can appear in them anywhere
  - even stripping the leading/trailing whitespaces is not safe to do: the first byte of the nVersion or the last byte of the nLocktime might look like whitespace too!
- instead, leading/trailing whitespaces can be stripped closer to where they are input, e.g. in the GUI
  - e.g. ".txn" files that we ourselves create contain a complete tx as a hex string, with a trailing final newline in the file
    - instead of reading that as bytes, we can read it as text
  - ".psbt" files OTOH are binary
This commit is contained in:
SomberNight
2025-12-11 16:40:15 +00:00
parent 16363cc3d9
commit 37db6ea7e8
3 changed files with 46 additions and 32 deletions

View File

@@ -2368,13 +2368,26 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
)
if not fileName:
return
file_content = None # type: None | str | bytes
# 1. try to open file as "text"
try:
with open(fileName, "rb") as f:
file_content = f.read() # type: Union[str, bytes]
with open(fileName, "r", encoding="ascii") as f:
file_content = f.read() # type: str
except (ValueError, IOError, os.error) as reason:
self.show_critical(_("Electrum was unable to open your transaction file") + "\n" + str(reason),
title=_("Unable to read file or no transaction found"))
return
pass
else:
assert isinstance(file_content, str), f"expected str, got {type(file_content)}"
file_content = file_content.strip() # for text, we can safely strip leading/trailing whitespaces
# 2. try to open file as "binary"
if file_content is None:
try:
with open(fileName, "rb") as f:
file_content = f.read() # type: bytes
except (ValueError, IOError, os.error) as reason:
self.show_critical(_("Electrum was unable to open your transaction file") + "\n" + str(reason),
title=_("Unable to read file or no transaction found"))
if file_content is None:
return None
return self.tx_from_text(file_content)
def do_process_from_text(self):

View File

@@ -1483,14 +1483,15 @@ def convert_raw_tx_to_hex(raw: Union[str, bytes]) -> str:
if not raw:
raise ValueError("empty string")
raw_unstripped = raw
# try to remove whitespaces.
# FIXME we should NOT do *any* whitespace mangling for bytes-like inputs, only str
raw = raw.strip() # remove leading/trailing whitespace, even if bytes-like
if isinstance(raw, str): # remove all whitespace characters, if str
if isinstance(raw, str):
# remove all whitespace characters, anywhere, for convenience
# - leading/trailing whitespaces are quite common for user-input
# - newlines in the middle can also happen, e.g. when copying a raw tx from a pdf
# note: we don't do this for bytes-like inputs, as whitespace-looking bytes can appear
# anywhere in a raw tx. Even leading/trailing pseudo-whitespace: consider that
# the nVersion or the nLocktime might contain "0a" bytes
# consider e.g.: "\n".encode().hex() == "0a"
# the nVersion or the nLocktime might contain e.g. "0a" bytes
# consider: "\n".encode().hex() == "0a"
# For str, this is a non-issue and safe to do.
raw = re.sub(r'\s', '', raw)
# try hex
try: