diff --git a/common/protob/messages-monero.proto b/common/protob/messages-monero.proto index 8fec14b68b..7013d63906 100644 --- a/common/protob/messages-monero.proto +++ b/common/protob/messages-monero.proto @@ -81,6 +81,7 @@ message MoneroGetAddress { optional uint32 network_type = 3; // Main-net / testnet / stagenet optional uint32 account = 4; // Major subaddr index optional uint32 minor = 5; // Minor subaddr index + optional bytes payment_id = 6; // Payment ID for integrated address } /** diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 5448f8a4c9..e5cd4bf529 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -1,8 +1,11 @@ from trezor.messages.MoneroAddress import MoneroAddress from apps.common import paths -from apps.common.layout import address_n_to_str, show_address, show_qr +from apps.common.layout import address_n_to_str, show_qr from apps.monero import CURVE, misc +from apps.monero.layout import confirms +from apps.monero.xmr import addresses, crypto, monero +from apps.monero.xmr.networks import net_version async def get_address(ctx, msg, keychain): @@ -11,13 +14,38 @@ async def get_address(ctx, msg, keychain): ) creds = misc.get_creds(keychain, msg.address_n, msg.network_type) + addr = creds.address + + if msg.payment_id: + if len(msg.payment_id) != 8: + raise ValueError("Invalid payment ID length") + addr = addresses.encode_addr( + net_version(msg.network_type, False, True), + crypto.encodepoint(creds.spend_key_public), + crypto.encodepoint(creds.view_key_public), + msg.payment_id, + ) + + if msg.account or msg.minor: + if msg.payment_id: + raise ValueError("Subaddress cannot be integrated") + + pub_spend, pub_view = monero.generate_sub_address_keys( + creds.view_key_private, creds.spend_key_public, msg.account, msg.minor + ) + + addr = addresses.encode_addr( + net_version(msg.network_type, True, False), + crypto.encodepoint(pub_spend), + crypto.encodepoint(pub_view), + ) if msg.show_display: desc = address_n_to_str(msg.address_n) while True: - if await show_address(ctx, creds.address.decode(), desc=desc): + if await confirms.show_address(ctx, addr.decode(), desc=desc): break - if await show_qr(ctx, creds.address.decode(), desc=desc): + if await show_qr(ctx, "monero:" + addr.decode(), desc=desc): break - return MoneroAddress(address=creds.address) + return MoneroAddress(address=addr) diff --git a/core/src/apps/monero/layout/common.py b/core/src/apps/monero/layout/common.py index 1820f002ff..05bc7a3c02 100644 --- a/core/src/apps/monero/layout/common.py +++ b/core/src/apps/monero/layout/common.py @@ -41,6 +41,7 @@ async def naive_pagination( def paginate_lines(lines, lines_per_page=5): + """Paginates lines across pages with preserving formatting modifiers (e.g., mono)""" pages = [] cpage = [] nlines = 0 diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index c79a715e9f..41b905b808 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -194,3 +194,28 @@ async def live_refresh_step(ctx, current): if current is None: return await Popup(LiveRefreshStep(current)) + + +async def show_address( + ctx, address: str, desc: str = "Confirm address", network: str = None +): + from apps.common.confirm import confirm + from trezor.messages import ButtonRequestType + from trezor.ui.button import ButtonDefault + from trezor.ui.scroll import Paginated + + pages = [] + for lines in common.paginate_lines(common.split_address(address), 5): + text = Text(desc, ui.ICON_RECEIVE, ui.GREEN) + if network is not None: + text.normal("%s network" % network) + text.mono(*lines) + pages.append(text) + + return await confirm( + ctx, + Paginated(pages), + code=ButtonRequestType.Address, + cancel="QR", + cancel_style=ButtonDefault, + ) diff --git a/core/src/trezor/messages/MoneroGetAddress.py b/core/src/trezor/messages/MoneroGetAddress.py index 013cc422c3..3aa517d843 100644 --- a/core/src/trezor/messages/MoneroGetAddress.py +++ b/core/src/trezor/messages/MoneroGetAddress.py @@ -19,12 +19,14 @@ class MoneroGetAddress(p.MessageType): network_type: int = None, account: int = None, minor: int = None, + payment_id: bytes = None, ) -> None: self.address_n = address_n if address_n is not None else [] self.show_display = show_display self.network_type = network_type self.account = account self.minor = minor + self.payment_id = payment_id @classmethod def get_fields(cls): @@ -34,4 +36,5 @@ class MoneroGetAddress(p.MessageType): 3: ('network_type', p.UVarintType, 0), 4: ('account', p.UVarintType, 0), 5: ('minor', p.UVarintType, 0), + 6: ('payment_id', p.BytesType, 0), } diff --git a/python/trezorlib/messages/MoneroGetAddress.py b/python/trezorlib/messages/MoneroGetAddress.py index 112369b8a8..48964c62c7 100644 --- a/python/trezorlib/messages/MoneroGetAddress.py +++ b/python/trezorlib/messages/MoneroGetAddress.py @@ -19,12 +19,14 @@ class MoneroGetAddress(p.MessageType): network_type: int = None, account: int = None, minor: int = None, + payment_id: bytes = None, ) -> None: self.address_n = address_n if address_n is not None else [] self.show_display = show_display self.network_type = network_type self.account = account self.minor = minor + self.payment_id = payment_id @classmethod def get_fields(cls): @@ -34,4 +36,5 @@ class MoneroGetAddress(p.MessageType): 3: ('network_type', p.UVarintType, 0), 4: ('account', p.UVarintType, 0), 5: ('minor', p.UVarintType, 0), + 6: ('payment_id', p.BytesType, 0), }