Skip to content

Commit

Permalink
Merge branch 'develop' into capture_qr_refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
odudex committed Jul 30, 2024
2 parents b347a59 + 268cc73 commit ddbe018
Show file tree
Hide file tree
Showing 12 changed files with 424 additions and 132 deletions.
28 changes: 16 additions & 12 deletions src/krux/pages/file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def select_file(

if path != SD_ROOT_PATH:
items.append("..")
menu_items.append(("..", lambda: MENU_EXIT))
menu_items.append(("../", lambda: MENU_EXIT))

# sorts by name ignorecase
dir_files = sorted(os.listdir(path), key=str.lower)
Expand All @@ -78,8 +78,10 @@ def select_file(

del dir_files

# show sorted folders first than sorted files
for filename in directories + files:
# show sorted folders first then sorted files
for i, filename in enumerate(directories + files):
is_directory = i < len(directories)

extension_match = False
if isinstance(file_extension, str):
# No extension filter or matches
Expand All @@ -91,18 +93,20 @@ def select_file(
extension_match = True
break

if (
extension_match
# Is a directory
or SDHandler.dir_exists(path + "/" + filename)
):
if extension_match or is_directory:
items.append(filename)
display_filename = filename
if len(filename) >= custom_start_digits + 2 + custom_end_digts:
display_filename = filename + "/" if is_directory else filename

if (
len(display_filename)
>= custom_start_digits + 2 + custom_end_digts
):
display_filename = (
filename[:custom_start_digits]
display_filename[:custom_start_digits]
+ ".."
+ filename[len(filename) - custom_end_digts :]
+ display_filename[
len(display_filename) - custom_end_digts :
]
)
menu_items.append(
(
Expand Down
61 changes: 41 additions & 20 deletions src/krux/pages/home_pages/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,13 @@ def load_psbt(self):

# If load_method == LOAD_FROM_SD
from ..utils import Utils
from ...sd_card import PSBT_FILE_EXTENSION
from ...sd_card import PSBT_FILE_EXTENSION, B64_PSBT_FILE_EXTENSION

utils = Utils(self.ctx)
psbt_filename, _ = utils.load_file(
PSBT_FILE_EXTENSION, prompt=False, only_get_filename=True
[PSBT_FILE_EXTENSION, B64_PSBT_FILE_EXTENSION],
prompt=False,
only_get_filename=True,
)
return (None, FORMAT_NONE, psbt_filename)

Expand All @@ -235,12 +237,35 @@ def _sign_menu(self):
index, _ = sign_menu.run_loop()
return index

def sign_psbt(self):
"""Handler for the 'sign psbt' menu item"""
def _format_psbt_file_extension(self, psbt_filename=""):
"""Formats the PSBT filename"""
from ...sd_card import (
PSBT_FILE_EXTENSION,
B64_PSBT_FILE_EXTENSION,
SIGNED_FILE_SUFFIX,
)
from ..file_operations import SaveFile

if psbt_filename.endswith(B64_PSBT_FILE_EXTENSION):
# Remove chained extensions
psbt_filename = psbt_filename[: -len(B64_PSBT_FILE_EXTENSION)]
if psbt_filename.endswith(PSBT_FILE_EXTENSION):
psbt_filename = psbt_filename[: -len(PSBT_FILE_EXTENSION)]
extension = PSBT_FILE_EXTENSION + B64_PSBT_FILE_EXTENSION
else:
extension = PSBT_FILE_EXTENSION

save_page = SaveFile(self.ctx)
psbt_filename = save_page.set_filename(
psbt_filename,
"QRCode",
SIGNED_FILE_SUFFIX,
extension,
)
return psbt_filename

def sign_psbt(self):
"""Handler for the 'sign psbt' menu item"""

# Warns in case multisig wallet descriptor is not loaded
if not self.ctx.wallet.is_loaded() and self.ctx.wallet.is_multisig():
Expand Down Expand Up @@ -368,37 +393,33 @@ def sign_psbt(self):
title = t("Signed PSBT")
if index == 0:
# Sign to QR code
qr_signed_psbt, qr_format = signer.psbt_qr()
signed_psbt, qr_format = signer.psbt_qr()

# memory management
del signer
gc.collect()

self.display_qr_codes(qr_signed_psbt, qr_format)
self.display_qr_codes(signed_psbt, qr_format)

from ..utils import Utils

utils = Utils(self.ctx)
utils.print_standard_qr(qr_signed_psbt, qr_format, title, width=45)
utils.print_standard_qr(signed_psbt, qr_format, title, width=45)
return MENU_CONTINUE

# index == 1: Sign to SD card
from ..file_operations import SaveFile

save_page = SaveFile(self.ctx)
psbt_filename = save_page.set_filename(
psbt_filename,
"QRCode",
SIGNED_FILE_SUFFIX,
PSBT_FILE_EXTENSION,
)
del save_page
psbt_filename = self._format_psbt_file_extension(psbt_filename)
gc.collect()

if psbt_filename and psbt_filename != ESC_KEY:
with open("/sd/" + psbt_filename, "wb") as f:
# Write PSBT data directly to the file
signer.psbt.write_to(f)
if signer.is_b64_file:
signed_psbt, _ = signer.psbt_qr()
with open("/sd/" + psbt_filename, "w") as f:
f.write(signed_psbt)
else:
with open("/sd/" + psbt_filename, "wb") as f:
# Write PSBT data directly to the file
signer.psbt.write_to(f)
self.flash_text(t("Saved to SD card") + ":\n%s" % psbt_filename)

return MENU_CONTINUE
Expand Down
7 changes: 6 additions & 1 deletion src/krux/pages/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,11 +726,16 @@ def settings(self):
def about(self):
"""Handler for the 'about' menu item"""

import board
from ..metadata import VERSION

self.ctx.display.clear()
self.ctx.display.draw_centered_text(
"Krux\n\n\n" + t("Version") + "\n%s" % VERSION
"Krux\n\n"
+ t("Hardware")
+ "\n%s\n\n" % board.config["type"]
+ t("Version")
+ "\n%s" % VERSION
)
self.ctx.input.wait_for_button()
return MENU_CONTINUE
47 changes: 37 additions & 10 deletions src/krux/psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,37 @@ def __init__(self, wallet, psbt_data, qr_format, psbt_filename=None):
self.ur_type = None
self.qr_format = qr_format
self.policy = None
self.is_b64_file = False

# Parse the PSBT
if psbt_filename:
gc.collect()
from .sd_card import SD_PATH

file_path = "/%s/%s" % (SD_PATH, psbt_filename)
try:
file_path = "/%s/%s" % (SD_PATH, psbt_filename)
with open(file_path, "rb") as file:
self.psbt = PSBT.read_from(file, compress=1)
try:
self.validate()
except:
self.validate()
except:
try:
self.policy = None # Reset policy
self.is_b64_file = self.file_is_base64_encoded(file_path)
if self.is_b64_file:
# BlueWallet exports PSBTs as base64 encoded files
# So it will be decoded and loaded uncompressed
with open(file_path, "r") as file:
psbt_data = file.read()
self.psbt = PSBT.parse(base_decode(psbt_data, 64))
else:
# Legacy will fail to get policy from compressed PSBT
# so we load it uncompressed
self.policy = None # Reset policy
file.seek(0) # Reset the file pointer to the beginning
self.psbt = PSBT.read_from(file)
self.base_encoding = 64 # In case it is exported as QR code
except Exception as e:
raise ValueError("Error loading PSBT file: %s" % e)
with open(file_path, "rb") as file:
file.seek(0) # Reset the file pointer to the beginning
self.psbt = PSBT.read_from(file)
except Exception as e:
raise ValueError("Error loading PSBT file: %s" % e)
self.base_encoding = 64 # In case it is exported as QR code
elif isinstance(psbt_data, UR):
try:
self.psbt = PSBT.parse(
Expand Down Expand Up @@ -114,6 +125,22 @@ def __init__(self, wallet, psbt_data, qr_format, psbt_filename=None):
except Exception as e:
raise ValueError("Invalid PSBT: %s" % e)

def file_is_base64_encoded(self, file_path, chunk_size=64):
"""Checks if a file is base64 encoded"""
with open(file_path, "rb") as file:
chunk = file.read(chunk_size)
if not chunk:
raise ValueError("Empty file")
# Check if chunk length is divisible by 4
if len(chunk) % 4 != 0:
return False
try:
# Try to decode the chunk as base64
base_decode(chunk, 64)
return True
except Exception:
return False

def validate(self):
"""Validates the PSBT"""
# From: https://github.com/diybitcoinhardware/embit/blob/master/examples/change.py#L110
Expand Down
1 change: 1 addition & 0 deletions src/krux/sd_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

SIGNED_FILE_SUFFIX = "-signed"
PSBT_FILE_EXTENSION = ".psbt"
B64_PSBT_FILE_EXTENSION = ".txt"
DESCRIPTOR_FILE_EXTENSION = ".txt"
JSON_FILE_EXTENSION = ".json"
SIGNATURE_FILE_EXTENSION = ".sig"
Expand Down
9 changes: 9 additions & 0 deletions src/krux/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ def parse_wallet(wallet_data, allow_assumption=None):
except:
pass

# Try to parse as a Crypto-Account type
try:
account = urtypes.crypto.Account.from_cbor(
wallet_data.cbor
).output_descriptors[0]
return Descriptor.from_string(account.descriptor()), None
except:
pass

# Treat the UR as a generic UR bytes object and extract the data for further processing
wallet_data = urtypes.Bytes.from_cbor(wallet_data.cbor).data

Expand Down
Loading

0 comments on commit ddbe018

Please sign in to comment.