Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/seedsigner/models/psbt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __init__(self, p: PSBT, seed: Seed, network: str = SettingsConstants.MAINNET
self.destination_addresses = []
self.destination_amounts = []
self.op_return_data: bytes = None
self.dnssec_proof: str = None

self.root = None

Expand Down Expand Up @@ -115,6 +116,8 @@ def _parse_outputs(self):
self.fee_amount = 0
self.destination_addresses = []
self.destination_amounts = []
self.dnssec_proof = None

for i, out in enumerate(self.psbt.outputs):
out_policy = PSBTParser._get_policy(out, self.psbt.tx.vout[i].script_pubkey, self.psbt.xpubs)
is_change = False
Expand Down Expand Up @@ -219,6 +222,9 @@ def _parse_outputs(self):
self.destination_amounts.append(self.psbt.tx.vout[i].value)
self.spend_amount += self.psbt.tx.vout[i].value

if out.dnssec_proof:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line will raise an error in all PSBT parser test cases until diybitcoinhardware/embit#102 gets merged

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should I handle this? Put a try and except?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this functionality depends on the embit PR, this PR should only be merged into SeedSigner once those changes are merged into embit. So there’s no immediate need to add temporary error handling here.

self.dnssec_proof = out.dnssec_proof

self.fee_amount = self.psbt.fee()
return True

Expand Down
64 changes: 63 additions & 1 deletion src/seedsigner/views/psbt_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def run(self):
# Everything is set. Stop the loading screen
if self.loading_screen:
self.loading_screen.stop()

# Run the overview screen
selected_menu_num = self.run_screen(
PSBTOverviewScreen,
Expand All @@ -160,6 +160,7 @@ def run(self):
return Destination(PSBTNoChangeWarningView)

else:
if psbt_parser.dnssec_proof: return Destination(PSBTDnsNameView)
return Destination(PSBTMathView)


Expand Down Expand Up @@ -204,6 +205,38 @@ def run(self):



class PSBTDnsNameView(View):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name may be too generic. It'd make sense to use something more related to BIP-353. Maybe PSBTHrnView or PSBTDnsPaymentIntructionsView

def run(self):
from seedsigner.gui.screens.screen import LargeIconStatusScreen
from embit.bip353 import verify_dns_proof
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line will raise an error until diybitcoinhardware/embit#102 gets merged


psbt_parser: PSBTParser = self.controller.psbt_parser
if not psbt_parser:
# Should not be able to get here
return Destination(MainMenuView)

dnssec_data = psbt_parser.dnssec_proof
dns_name = dnssec_data[0].decode('utf-8').replace('@', '.user._bitcoin-payment.')
proof = dnssec_data[1]

verified = verify_dns_proof(dns_name+'.', proof)
Copy link
Contributor

@alvroble alvroble Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More checking should happen here. What if the dns_name is not the same as the verified by the proof? I'm not sure if we should display the hrn from from the PSBT field or from the proof.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood it correctly and as explained in #768 , the dns_name should not be extracted from the proof as it may be different of the dns_name of the PSBT. When validating, we look if the proof allows that name. The way I am doing this is to first look if the verification was successful and then check if the proof allows for that name. I do that in the next line when checking if verified['verified_rrs'] have any records, as that would mean the proof is valid for that name. Do you think my approach is correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now, you are right. dns_name should always come from the PSBT, and the proof just gets validated against that value. We don’t extract or compare a name from the proof itself. So my earlier note isn’t needed here. The key check is simply whether the proof validates for the PSBT’s dns_name, which is done entirely by dnssec-prover.

if not 'error' in verified and len(verified['verified_rrs']) > 0: # Check if there is a valid record for that name in the proof
selected_menu_num = LargeIconStatusScreen(
title = _("DNS name verification"),
button_data = [ButtonOption("Review math")],
text = _("Verification complete. Review the DNS name."),
status_headline = dns_name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will print out pay.user._bitcoin-payment.satsto.me for HRN pay@satsto.me.

SHOULD display ₿pay@satsto.me. Currently ₿ displays as an unknown character in SeedSigner, so maybe it makes sense to leave it as pay@satsto.me for now.

).display()
else:
return Destination(PSBTDnsNameErrorView)

if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
else:
return Destination(PSBTMathView)



class PSBTMathView(View):
"""
Follows the Overview pictogram. Shows:
Expand Down Expand Up @@ -594,3 +627,32 @@ def run(self):

if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)



class PSBTDnsNameErrorView(View):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same about the naming. As it is an error view, should use a name that identifies the reason of error which is an invalid DNSSEC proof for the provided HRN. Maybe PSBTDnssecProofError or PSBTDnsPaymentIntructionsError

SELECT_DIFF_SEED = ButtonOption("Return")

def run(self):
psbt_parser: PSBTParser = self.controller.psbt_parser
if not psbt_parser:
# Should not be able to get here
return Destination(MainMenuView)

# Just a WarningScreen here; only use DireWarningScreen for true security risks.
selected_menu_num = self.run_screen(
WarningScreen,
title=_("DNS name Error"),
status_icon_name=SeedSignerIconConstants.WARNING,
status_headline=_("DENSSEC verification Failed"),
text=_("Verification not compelted. Verify the DNS name and the proof."),
button_data=[self.SELECT_DIFF_SEED]
)

if selected_menu_num == 0:
# clear seed selected for psbt signing since it did not add a valid signature
self.controller.psbt_seed = None
return Destination(MainMenuView, clear_history=True)

if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(MainMenuView)
Loading