From f013340a25eadc47b11f47441a73e9cfab46ebd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Mon, 7 Oct 2024 13:23:49 +0200 Subject: [PATCH] feat(core): introduce Nostr [no changelog] --- common/protob/messages-nostr.proto | 64 + common/protob/messages.proto | 6 + core/.changelog.d/4160.added | 1 + core/SConscript.firmware | 4 + core/SConscript.unix | 4 + .../generated/translated_string.rs.mako | 1 + core/src/all_modules.py | 6 + core/src/apps/nostr/__init__.py | 9 + core/src/apps/nostr/get_pubkey.py | 24 + core/src/apps/nostr/sign_event.py | 48 + core/src/apps/workflow_handlers.py | 6 + core/src/trezor/enums/MessageType.py | 4 + core/src/trezor/enums/__init__.py | 4 + core/src/trezor/messages.py | 86 ++ legacy/firmware/protob/Makefile | 3 +- python/src/trezorlib/cli/nostr.py | 93 ++ python/src/trezorlib/cli/trezorctl.py | 2 + python/src/trezorlib/messages.py | 98 ++ python/src/trezorlib/nostr.py | 40 + rust/trezor-client/scripts/build_messages | 3 +- rust/trezor-client/src/messages/generated.rs | 8 + .../src/protos/generated/messages.rs | 56 +- .../src/protos/generated/messages_nostr.rs | 1155 +++++++++++++++++ rust/trezor-client/src/protos/mod.rs | 1 + tests/device_tests/nostr/__init__.py | 0 tests/device_tests/nostr/test_nostr.py | 129 ++ 26 files changed, 1839 insertions(+), 16 deletions(-) create mode 100644 common/protob/messages-nostr.proto create mode 100644 core/.changelog.d/4160.added create mode 100644 core/src/apps/nostr/__init__.py create mode 100644 core/src/apps/nostr/get_pubkey.py create mode 100644 core/src/apps/nostr/sign_event.py create mode 100644 python/src/trezorlib/cli/nostr.py create mode 100644 python/src/trezorlib/nostr.py create mode 100644 rust/trezor-client/src/protos/generated/messages_nostr.rs create mode 100644 tests/device_tests/nostr/__init__.py create mode 100644 tests/device_tests/nostr/test_nostr.py diff --git a/common/protob/messages-nostr.proto b/common/protob/messages-nostr.proto new file mode 100644 index 0000000000..f8b5a599bf --- /dev/null +++ b/common/protob/messages-nostr.proto @@ -0,0 +1,64 @@ +syntax = "proto2"; +package hw.trezor.messages.nostr; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageNostr"; + +/** + * Request: Ask the device for the Nostr public key + * @start + * @next NostrPubkey + */ +message NostrGetPubkey { + repeated uint32 address_n = 1; // used to derive the key +} + +/** + * Response: Nostr pubkey + * @end + */ +message NostrPubkey { + required bytes pubkey = 1; // pubkey derived from the seed +} + +/** + * @embed + */ +message NostrTag { + // Nostr tags consist of at least one string (the key) + // followed by an arbitrary number of strings, + // the first of which (if present) is called "value". + // See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md#tags + required string key = 1; + optional string value = 2; + repeated string extra = 3; +} + +/** + * Request: Ask device to sign an event + * @start + * @next NostrEventSignature + * @next Failure + */ +message NostrSignEvent { + repeated uint32 address_n = 1; // used to derive the key + + // Nostr event fields, except the ones that are calculated by the signer + // See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md + + required uint32 created_at = 2; // Event created_at: unix timestamp in seconds + required uint32 kind = 3; // Event kind: integer between 0 and 65535 + repeated NostrTag tags = 4; // Event tags + required string content = 5; // Event content: arbitrary string +} + +/** + * Response: Computed event ID and signature + * @end + */ +message NostrEventSignature { + required bytes pubkey = 1; // pubkey used to sign the event + required bytes id = 2; // ID of the event + required bytes signature = 3; // signature of the event +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index ae7fdfda59..ee53109a3c 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -324,6 +324,12 @@ enum MessageType { // THP reserved 1000 to 1099; // See messages-thp.proto + // Nostr + MessageType_NostrGetPubkey = 2001 [(wire_in) = true]; + MessageType_NostrPubkey = 2002 [(wire_out) = true]; + MessageType_NostrSignEvent = 2003 [(wire_in) = true]; + MessageType_NostrEventSignature = 2004 [(wire_out) = true]; + // Benchmark MessageType_BenchmarkListNames = 9100 [(bitcoin_only) = true]; MessageType_BenchmarkNames = 9101 [(bitcoin_only) = true]; diff --git a/core/.changelog.d/4160.added b/core/.changelog.d/4160.added new file mode 100644 index 0000000000..7879e132f6 --- /dev/null +++ b/core/.changelog.d/4160.added @@ -0,0 +1 @@ +Add Nostr support (in debug mode only!). diff --git a/core/SConscript.firmware b/core/SConscript.firmware index aa98a85721..93ce77eb3d 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -662,6 +662,10 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Nostr*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py')) diff --git a/core/SConscript.unix b/core/SConscript.unix index 86a2111eef..00227af2dd 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -721,6 +721,10 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nem/*/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/NEM*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/nostr/*/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Nostr*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ripple/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Ripple*.py')) diff --git a/core/embed/rust/src/translations/generated/translated_string.rs.mako b/core/embed/rust/src/translations/generated/translated_string.rs.mako index 78e4ca7d3c..62ac106a3f 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs.mako +++ b/core/embed/rust/src/translations/generated/translated_string.rs.mako @@ -15,6 +15,7 @@ ALTCOIN_PREFIXES = ( "fido", "monero", "nem", + "nostr", "ripple", "solana", "stellar", diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 70651ea3a8..33bdc38243 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -415,6 +415,12 @@ apps.misc.get_firmware_hash import apps.misc.get_firmware_hash apps.misc.sign_identity import apps.misc.sign_identity +apps.nostr +import apps.nostr +apps.nostr.get_pubkey +import apps.nostr.get_pubkey +apps.nostr.sign_event +import apps.nostr.sign_event apps.workflow_handlers import apps.workflow_handlers diff --git a/core/src/apps/nostr/__init__.py b/core/src/apps/nostr/__init__.py new file mode 100644 index 0000000000..98377d3a9c --- /dev/null +++ b/core/src/apps/nostr/__init__.py @@ -0,0 +1,9 @@ +from apps.common.paths import PATTERN_BIP44 + +CURVE = "secp256k1" +SLIP44_ID = 1237 + +# Note: we are not satisfied with using this path even though it is defined in NIP-06. +# See this issue for details: https://github.com/nostr-protocol/nips/issues/1774 +# TODO: we need to create a new NIP using a different derivation path and use that here! +PATTERN = PATTERN_BIP44 diff --git a/core/src/apps/nostr/get_pubkey.py b/core/src/apps/nostr/get_pubkey.py new file mode 100644 index 0000000000..467f27cefc --- /dev/null +++ b/core/src/apps/nostr/get_pubkey.py @@ -0,0 +1,24 @@ +from typing import TYPE_CHECKING + +from apps.common.keychain import auto_keychain + +if TYPE_CHECKING: + from trezor.messages import NostrGetPubkey, NostrPubkey + + from apps.common.keychain import Keychain + + +@auto_keychain(__name__) +async def get_pubkey(msg: NostrGetPubkey, keychain: Keychain) -> NostrPubkey: + from trezor.messages import NostrPubkey + + from apps.common import paths + + address_n = msg.address_n + + await paths.validate_path(keychain, address_n) + + node = keychain.derive(address_n) + pk = node.public_key()[-32:] + + return NostrPubkey(pubkey=pk) diff --git a/core/src/apps/nostr/sign_event.py b/core/src/apps/nostr/sign_event.py new file mode 100644 index 0000000000..26a2f5e0f6 --- /dev/null +++ b/core/src/apps/nostr/sign_event.py @@ -0,0 +1,48 @@ +from typing import TYPE_CHECKING + +from apps.common.keychain import auto_keychain + +if TYPE_CHECKING: + from trezor.messages import NostrEventSignature, NostrSignEvent + + from apps.common.keychain import Keychain + + +@auto_keychain(__name__) +async def sign_event(msg: NostrSignEvent, keychain: Keychain) -> NostrEventSignature: + from ubinascii import hexlify + + from trezor.crypto.curve import secp256k1 + from trezor.crypto.hashlib import sha256 + from trezor.messages import NostrEventSignature + + from apps.common import paths + + address_n = msg.address_n + created_at = msg.created_at + kind = msg.kind + tags = [[t.key] + ([t.value] if t.value else []) + t.extra for t in msg.tags] + content = msg.content + + await paths.validate_path(keychain, address_n) + + node = keychain.derive(address_n) + pk = node.public_key()[-32:] + + # The event ID is obtained by serializing the event in a specific way: + # "[0,pubkey,created_at,kind,tags,content]" + # See NIP-01: https://github.com/nostr-protocol/nips/blob/master/01.md + serialized_tags = ",".join( + ["[" + ",".join(f'"{t}"' for t in tag) + "]" for tag in tags] + ) + serialized_event = f'[0,"{hexlify(pk).decode()}",{created_at},{kind},[{serialized_tags}],"{content}"]' + event_id = sha256(serialized_event).digest() + + # The event signature is basically the signature of the event ID computed above + signature = secp256k1.sign(sk, event_id)[-64:] + + return NostrEventSignature( + pubkey=pk, + id=event_id, + signature=signature, + ) diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index b65c853c93..af8460791b 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -106,6 +106,12 @@ def _find_message_handler_module(msg_type: int) -> str: if msg_type == MessageType.GetFirmwareHash: return "apps.misc.get_firmware_hash" + # nostr + if msg_type == MessageType.NostrGetPubkey: + return "apps.nostr.get_pubkey" + if msg_type == MessageType.NostrSignEvent: + return "apps.nostr.sign_event" + if not utils.BITCOIN_ONLY: if msg_type == MessageType.SetU2FCounter: return "apps.management.set_u2f_counter" diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index ed569795b0..14de4cf790 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -250,3 +250,7 @@ if not utils.BITCOIN_ONLY: SolanaAddress = 903 SolanaSignTx = 904 SolanaTxSignature = 905 + NostrGetPubkey = 2001 + NostrPubkey = 2002 + NostrSignEvent = 2003 + NostrEventSignature = 2004 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index d16c3c4a66..1ed220d2ad 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -591,6 +591,10 @@ if TYPE_CHECKING: SolanaAddress = 903 SolanaSignTx = 904 SolanaTxSignature = 905 + NostrGetPubkey = 2001 + NostrPubkey = 2002 + NostrSignEvent = 2003 + NostrEventSignature = 2004 BenchmarkListNames = 9100 BenchmarkNames = 9101 BenchmarkRun = 9102 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 1dbfcbc407..9d4c93f782 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -5216,6 +5216,92 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["NEMCosignatoryModification"]: return isinstance(msg, cls) + class NostrGetPubkey(protobuf.MessageType): + address_n: "list[int]" + + def __init__( + self, + *, + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrGetPubkey"]: + return isinstance(msg, cls) + + class NostrPubkey(protobuf.MessageType): + pubkey: "bytes" + + def __init__( + self, + *, + pubkey: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrPubkey"]: + return isinstance(msg, cls) + + class NostrTag(protobuf.MessageType): + key: "str" + value: "str | None" + extra: "list[str]" + + def __init__( + self, + *, + key: "str", + extra: "list[str] | None" = None, + value: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrTag"]: + return isinstance(msg, cls) + + class NostrSignEvent(protobuf.MessageType): + address_n: "list[int]" + created_at: "int" + kind: "int" + tags: "list[NostrTag]" + content: "str" + + def __init__( + self, + *, + created_at: "int", + kind: "int", + content: "str", + address_n: "list[int] | None" = None, + tags: "list[NostrTag] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrSignEvent"]: + return isinstance(msg, cls) + + class NostrEventSignature(protobuf.MessageType): + pubkey: "bytes" + id: "bytes" + signature: "bytes" + + def __init__( + self, + *, + pubkey: "bytes", + id: "bytes", + signature: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["NostrEventSignature"]: + return isinstance(msg, cls) + class RippleGetAddress(protobuf.MessageType): address_n: "list[int]" show_display: "bool | None" diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 48dbd5ddc7..16c959d834 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -12,7 +12,8 @@ SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdPro Solana StellarClaimClaimableBalanceOp \ ChangeLanguage TranslationDataRequest TranslationDataAck \ SetBrightness DebugLinkOptigaSetSecMax EntropyCheckReady EntropyCheckContinue \ - BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult + BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult \ + NostrGetPubkey NostrPubkey NostrSignEvent NostrEventSignature ifeq ($(BITCOIN_ONLY), 1) SKIPPED_MESSAGES += Ethereum NEM Stellar diff --git a/python/src/trezorlib/cli/nostr.py b/python/src/trezorlib/cli/nostr.py new file mode 100644 index 0000000000..b52ac3b8a7 --- /dev/null +++ b/python/src/trezorlib/cli/nostr.py @@ -0,0 +1,93 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2025 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from __future__ import annotations + +import json +import typing as t + +import click + +from .. import messages, nostr, tools +from . import with_client + +if t.TYPE_CHECKING: + from ..client import TrezorClient + + +PATH_TEMPLATE = "m/44h/1237h/{}h/0/0" + + +@click.group(name="nostr") +def cli() -> None: + pass + + +@cli.command() +@click.option("-a", "--account", default=0, help="Account index") +@with_client +def get_pubkey( + client: "TrezorClient", + account: int, +) -> str: + """Return the pubkey derived by the given path.""" + + address_n = tools.parse_path(PATH_TEMPLATE.format(account)) + + return nostr.get_pubkey( + client, + address_n, + ).hex() + + +@cli.command() +@click.option("-a", "--account", default=0, help="Account index") +@click.argument("event") +@with_client +def sign_event( + client: "TrezorClient", + account: int, + event: str, +) -> dict[str, str]: + """Sign an event using the key derived by the given path.""" + + event_json = json.loads(event) + + address_n = tools.parse_path(PATH_TEMPLATE.format(account)) + + res = nostr.sign_event( + client, + messages.NostrSignEvent( + address_n=address_n, + created_at=event_json["created_at"], + kind=event_json["kind"], + tags=[ + messages.NostrTag( + key=t[0], value=t[1] if len(t) > 1 else None, extra=t[2:] + ) + for t in event_json["tags"] + ], + content=event_json["content"], + ), + ) + + event_json["id"] = res.id.hex() + event_json["pubkey"] = res.pubkey.hex() + event_json["sig"] = res.signature.hex() + + return { + "signed_event": event_json, + } diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index 60f8e8d309..d1f32a7c7f 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -44,6 +44,7 @@ from . import ( firmware, monero, nem, + nostr, ripple, settings, solana, @@ -409,6 +410,7 @@ cli.add_command(ethereum.cli) cli.add_command(fido.cli) cli.add_command(monero.cli) cli.add_command(nem.cli) +cli.add_command(nostr.cli) cli.add_command(ripple.cli) cli.add_command(settings.cli) cli.add_command(solana.cli) diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 024c3ae696..dedfaae21b 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -644,6 +644,10 @@ class MessageType(IntEnum): SolanaAddress = 903 SolanaSignTx = 904 SolanaTxSignature = 905 + NostrGetPubkey = 2001 + NostrPubkey = 2002 + NostrSignEvent = 2003 + NostrEventSignature = 2004 BenchmarkListNames = 9100 BenchmarkNames = 9101 BenchmarkRun = 9102 @@ -6775,6 +6779,100 @@ class NEMCosignatoryModification(protobuf.MessageType): self.public_key = public_key +class NostrGetPubkey(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 2001 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + } + + def __init__( + self, + *, + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + + +class NostrPubkey(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 2002 + FIELDS = { + 1: protobuf.Field("pubkey", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + pubkey: "bytes", + ) -> None: + self.pubkey = pubkey + + +class NostrTag(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("key", "string", repeated=False, required=True), + 2: protobuf.Field("value", "string", repeated=False, required=False, default=None), + 3: protobuf.Field("extra", "string", repeated=True, required=False, default=None), + } + + def __init__( + self, + *, + key: "str", + extra: Optional[Sequence["str"]] = None, + value: Optional["str"] = None, + ) -> None: + self.extra: Sequence["str"] = extra if extra is not None else [] + self.key = key + self.value = value + + +class NostrSignEvent(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 2003 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("created_at", "uint32", repeated=False, required=True), + 3: protobuf.Field("kind", "uint32", repeated=False, required=True), + 4: protobuf.Field("tags", "NostrTag", repeated=True, required=False, default=None), + 5: protobuf.Field("content", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + created_at: "int", + kind: "int", + content: "str", + address_n: Optional[Sequence["int"]] = None, + tags: Optional[Sequence["NostrTag"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.tags: Sequence["NostrTag"] = tags if tags is not None else [] + self.created_at = created_at + self.kind = kind + self.content = content + + +class NostrEventSignature(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 2004 + FIELDS = { + 1: protobuf.Field("pubkey", "bytes", repeated=False, required=True), + 2: protobuf.Field("id", "bytes", repeated=False, required=True), + 3: protobuf.Field("signature", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + pubkey: "bytes", + id: "bytes", + signature: "bytes", + ) -> None: + self.pubkey = pubkey + self.id = id + self.signature = signature + + class RippleGetAddress(protobuf.MessageType): MESSAGE_WIRE_TYPE = 400 FIELDS = { diff --git a/python/src/trezorlib/nostr.py b/python/src/trezorlib/nostr.py new file mode 100644 index 0000000000..1db2c2127b --- /dev/null +++ b/python/src/trezorlib/nostr.py @@ -0,0 +1,40 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2025 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + + +from typing import TYPE_CHECKING + +from . import messages + +if TYPE_CHECKING: + from .client import TrezorClient + from .tools import Address + + +def get_pubkey(client: "TrezorClient", n: "Address") -> bytes: + return client.call( + messages.NostrGetPubkey( + address_n=n, + ), + expect=messages.NostrPubkey, + ).pubkey + + +def sign_event( + client: "TrezorClient", + sign_event: messages.NostrSignEvent, +) -> messages.NostrEventSignature: + return client.call(sign_event, expect=messages.NostrEventSignature) diff --git a/rust/trezor-client/scripts/build_messages b/rust/trezor-client/scripts/build_messages index 33d736fe3a..9203783c5d 100755 --- a/rust/trezor-client/scripts/build_messages +++ b/rust/trezor-client/scripts/build_messages @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Generates the `trezor_message_impl!` macro calls for the `src/messages/mod.rs` file. +# Generates the `trezor_message_impl!` macro calls for the `src/messages/generated.rs` file. from os import path @@ -24,6 +24,7 @@ FEATURES = { "EOS": "eos", "Monero": "monero", "NEM": "nem", + "Nostr": "nostr", "Ripple": "ripple", "Solana": "solana", "Stellar": "stellar", diff --git a/rust/trezor-client/src/messages/generated.rs b/rust/trezor-client/src/messages/generated.rs index 551a1e92e2..814125c4dc 100644 --- a/rust/trezor-client/src/messages/generated.rs +++ b/rust/trezor-client/src/messages/generated.rs @@ -237,6 +237,14 @@ trezor_message_impl! { NEMDecryptedMessage => MessageType_NEMDecryptedMessage, } +#[cfg(feature = "nostr")] +trezor_message_impl! { + NostrGetPubkey => MessageType_NostrGetPubkey, + NostrPubkey => MessageType_NostrPubkey, + NostrSignEvent => MessageType_NostrSignEvent, + NostrEventSignature => MessageType_NostrEventSignature, +} + #[cfg(feature = "ripple")] trezor_message_impl! { RippleGetAddress => MessageType_RippleGetAddress, diff --git a/rust/trezor-client/src/protos/generated/messages.rs b/rust/trezor-client/src/protos/generated/messages.rs index 0a265410a6..e3797456aa 100644 --- a/rust/trezor-client/src/protos/generated/messages.rs +++ b/rust/trezor-client/src/protos/generated/messages.rs @@ -514,6 +514,14 @@ pub enum MessageType { MessageType_SolanaSignTx = 904, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SolanaTxSignature) MessageType_SolanaTxSignature = 905, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrGetPubkey) + MessageType_NostrGetPubkey = 2001, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrPubkey) + MessageType_NostrPubkey = 2002, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrSignEvent) + MessageType_NostrSignEvent = 2003, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_NostrEventSignature) + MessageType_NostrEventSignature = 2004, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkListNames) MessageType_BenchmarkListNames = 9100, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_BenchmarkNames) @@ -776,6 +784,10 @@ impl ::protobuf::Enum for MessageType { 903 => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress), 904 => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx), 905 => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature), + 2001 => ::std::option::Option::Some(MessageType::MessageType_NostrGetPubkey), + 2002 => ::std::option::Option::Some(MessageType::MessageType_NostrPubkey), + 2003 => ::std::option::Option::Some(MessageType::MessageType_NostrSignEvent), + 2004 => ::std::option::Option::Some(MessageType::MessageType_NostrEventSignature), 9100 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames), 9101 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames), 9102 => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun), @@ -1029,6 +1041,10 @@ impl ::protobuf::Enum for MessageType { "MessageType_SolanaAddress" => ::std::option::Option::Some(MessageType::MessageType_SolanaAddress), "MessageType_SolanaSignTx" => ::std::option::Option::Some(MessageType::MessageType_SolanaSignTx), "MessageType_SolanaTxSignature" => ::std::option::Option::Some(MessageType::MessageType_SolanaTxSignature), + "MessageType_NostrGetPubkey" => ::std::option::Option::Some(MessageType::MessageType_NostrGetPubkey), + "MessageType_NostrPubkey" => ::std::option::Option::Some(MessageType::MessageType_NostrPubkey), + "MessageType_NostrSignEvent" => ::std::option::Option::Some(MessageType::MessageType_NostrSignEvent), + "MessageType_NostrEventSignature" => ::std::option::Option::Some(MessageType::MessageType_NostrEventSignature), "MessageType_BenchmarkListNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkListNames), "MessageType_BenchmarkNames" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkNames), "MessageType_BenchmarkRun" => ::std::option::Option::Some(MessageType::MessageType_BenchmarkRun), @@ -1281,6 +1297,10 @@ impl ::protobuf::Enum for MessageType { MessageType::MessageType_SolanaAddress, MessageType::MessageType_SolanaSignTx, MessageType::MessageType_SolanaTxSignature, + MessageType::MessageType_NostrGetPubkey, + MessageType::MessageType_NostrPubkey, + MessageType::MessageType_NostrSignEvent, + MessageType::MessageType_NostrEventSignature, MessageType::MessageType_BenchmarkListNames, MessageType::MessageType_BenchmarkNames, MessageType::MessageType_BenchmarkRun, @@ -1539,10 +1559,14 @@ impl ::protobuf::EnumFull for MessageType { MessageType::MessageType_SolanaAddress => 240, MessageType::MessageType_SolanaSignTx => 241, MessageType::MessageType_SolanaTxSignature => 242, - MessageType::MessageType_BenchmarkListNames => 243, - MessageType::MessageType_BenchmarkNames => 244, - MessageType::MessageType_BenchmarkRun => 245, - MessageType::MessageType_BenchmarkResult => 246, + MessageType::MessageType_NostrGetPubkey => 243, + MessageType::MessageType_NostrPubkey => 244, + MessageType::MessageType_NostrSignEvent => 245, + MessageType::MessageType_NostrEventSignature => 246, + MessageType::MessageType_BenchmarkListNames => 247, + MessageType::MessageType_BenchmarkNames => 248, + MessageType::MessageType_BenchmarkRun => 249, + MessageType::MessageType_BenchmarkResult => 250, }; Self::enum_descriptor().value_by_index(index) } @@ -1561,7 +1585,7 @@ impl MessageType { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\xe8U\ + \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\x86W\ \n\x0bMessageType\x12(\n\x16MessageType_Initialize\x10\0\x1a\x0c\x80\xa6\ \x1d\x01\xb0\xb5\x18\x01\x90\xb5\x18\x01\x12\x1e\n\x10MessageType_Ping\ \x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12%\n\x13MessageType_S\ @@ -1838,15 +1862,19 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\x87\x07\x1a\x04\x98\xb5\ \x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\x88\x07\x1a\x04\x90\xb5\ \x18\x01\x12(\n\x1dMessageType_SolanaTxSignature\x10\x89\x07\x1a\x04\x98\ - \xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\x10\x8cG\x1a\x04\ - \x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\x10\x8dG\x1a\x04\ - \x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\x8eG\x1a\x04\x80\ - \xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\x8fG\x1a\x04\x80\ - \xa6\x1d\x01\x1a\x04\xc8\xf3\x18\x01\"\x04\x08Z\x10\\\"\x04\x08G\x10J\"\ - \x04\x08r\x10z\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\xe0\x01\x10\xe0\ - \x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\xb8\x02\"\x06\ - \x08\xe8\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.protobufB\rTrezo\ - rMessage\x80\xa6\x1d\x01\ + \xb5\x18\x01\x12%\n\x1aMessageType_NostrGetPubkey\x10\xd1\x0f\x1a\x04\ + \x90\xb5\x18\x01\x12\"\n\x17MessageType_NostrPubkey\x10\xd2\x0f\x1a\x04\ + \x98\xb5\x18\x01\x12%\n\x1aMessageType_NostrSignEvent\x10\xd3\x0f\x1a\ + \x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_NostrEventSignature\x10\xd4\ + \x0f\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkListNames\ + \x10\x8cG\x1a\x04\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\ + \x10\x8dG\x1a\x04\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\ + \x8eG\x1a\x04\x80\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\x10\ + \x8fG\x1a\x04\x80\xa6\x1d\x01\x1a\x04\xc8\xf3\x18\x01\"\x04\x08Z\x10\\\"\ + \x04\x08G\x10J\"\x04\x08r\x10z\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\ + \xe0\x01\x10\xe0\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\x10\ + \xb8\x02\"\x06\x08\xe8\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.pr\ + otobufB\rTrezorMessage\x80\xa6\x1d\x01\ "; /// `FileDescriptorProto` object which was a source for this generated file diff --git a/rust/trezor-client/src/protos/generated/messages_nostr.rs b/rust/trezor-client/src/protos/generated/messages_nostr.rs new file mode 100644 index 0000000000..05fffdf309 --- /dev/null +++ b/rust/trezor-client/src/protos/generated/messages_nostr.rs @@ -0,0 +1,1155 @@ +// This file is generated by rust-protobuf 3.3.0. Do not edit +// .proto file is parsed by protoc 3.19.6 +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_results)] +#![allow(unused_mut)] + +//! Generated file from `messages-nostr.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0; + +// @@protoc_insertion_point(message:hw.trezor.messages.nostr.NostrGetPubkey) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct NostrGetPubkey { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrGetPubkey.address_n) + pub address_n: ::std::vec::Vec, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.nostr.NostrGetPubkey.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a NostrGetPubkey { + fn default() -> &'a NostrGetPubkey { + ::default_instance() + } +} + +impl NostrGetPubkey { + pub fn new() -> NostrGetPubkey { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(1); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "address_n", + |m: &NostrGetPubkey| { &m.address_n }, + |m: &mut NostrGetPubkey| { &mut m.address_n }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "NostrGetPubkey", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for NostrGetPubkey { + const NAME: &'static str = "NostrGetPubkey"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + is.read_repeated_packed_uint32_into(&mut self.address_n)?; + }, + 8 => { + self.address_n.push(is.read_uint32()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + for value in &self.address_n { + my_size += ::protobuf::rt::uint32_size(1, *value); + }; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + for v in &self.address_n { + os.write_uint32(1, *v)?; + }; + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> NostrGetPubkey { + NostrGetPubkey::new() + } + + fn clear(&mut self) { + self.address_n.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static NostrGetPubkey { + static instance: NostrGetPubkey = NostrGetPubkey { + address_n: ::std::vec::Vec::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for NostrGetPubkey { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("NostrGetPubkey").unwrap()).clone() + } +} + +impl ::std::fmt::Display for NostrGetPubkey { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for NostrGetPubkey { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.nostr.NostrPubkey) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct NostrPubkey { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrPubkey.pubkey) + pub pubkey: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.nostr.NostrPubkey.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a NostrPubkey { + fn default() -> &'a NostrPubkey { + ::default_instance() + } +} + +impl NostrPubkey { + pub fn new() -> NostrPubkey { + ::std::default::Default::default() + } + + // required bytes pubkey = 1; + + pub fn pubkey(&self) -> &[u8] { + match self.pubkey.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_pubkey(&mut self) { + self.pubkey = ::std::option::Option::None; + } + + pub fn has_pubkey(&self) -> bool { + self.pubkey.is_some() + } + + // Param is passed by value, moved + pub fn set_pubkey(&mut self, v: ::std::vec::Vec) { + self.pubkey = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_pubkey(&mut self) -> &mut ::std::vec::Vec { + if self.pubkey.is_none() { + self.pubkey = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.pubkey.as_mut().unwrap() + } + + // Take field + pub fn take_pubkey(&mut self) -> ::std::vec::Vec { + self.pubkey.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(1); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "pubkey", + |m: &NostrPubkey| { &m.pubkey }, + |m: &mut NostrPubkey| { &mut m.pubkey }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "NostrPubkey", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for NostrPubkey { + const NAME: &'static str = "NostrPubkey"; + + fn is_initialized(&self) -> bool { + if self.pubkey.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.pubkey = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.pubkey.as_ref() { + my_size += ::protobuf::rt::bytes_size(1, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.pubkey.as_ref() { + os.write_bytes(1, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> NostrPubkey { + NostrPubkey::new() + } + + fn clear(&mut self) { + self.pubkey = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static NostrPubkey { + static instance: NostrPubkey = NostrPubkey { + pubkey: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for NostrPubkey { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("NostrPubkey").unwrap()).clone() + } +} + +impl ::std::fmt::Display for NostrPubkey { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for NostrPubkey { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.nostr.NostrTag) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct NostrTag { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrTag.key) + pub key: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrTag.value) + pub value: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrTag.extra) + pub extra: ::std::vec::Vec<::std::string::String>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.nostr.NostrTag.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a NostrTag { + fn default() -> &'a NostrTag { + ::default_instance() + } +} + +impl NostrTag { + pub fn new() -> NostrTag { + ::std::default::Default::default() + } + + // required string key = 1; + + pub fn key(&self) -> &str { + match self.key.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_key(&mut self) { + self.key = ::std::option::Option::None; + } + + pub fn has_key(&self) -> bool { + self.key.is_some() + } + + // Param is passed by value, moved + pub fn set_key(&mut self, v: ::std::string::String) { + self.key = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_key(&mut self) -> &mut ::std::string::String { + if self.key.is_none() { + self.key = ::std::option::Option::Some(::std::string::String::new()); + } + self.key.as_mut().unwrap() + } + + // Take field + pub fn take_key(&mut self) -> ::std::string::String { + self.key.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // optional string value = 2; + + pub fn value(&self) -> &str { + match self.value.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_value(&mut self) { + self.value = ::std::option::Option::None; + } + + pub fn has_value(&self) -> bool { + self.value.is_some() + } + + // Param is passed by value, moved + pub fn set_value(&mut self, v: ::std::string::String) { + self.value = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_value(&mut self) -> &mut ::std::string::String { + if self.value.is_none() { + self.value = ::std::option::Option::Some(::std::string::String::new()); + } + self.value.as_mut().unwrap() + } + + // Take field + pub fn take_value(&mut self) -> ::std::string::String { + self.value.take().unwrap_or_else(|| ::std::string::String::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "key", + |m: &NostrTag| { &m.key }, + |m: &mut NostrTag| { &mut m.key }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "value", + |m: &NostrTag| { &m.value }, + |m: &mut NostrTag| { &mut m.value }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "extra", + |m: &NostrTag| { &m.extra }, + |m: &mut NostrTag| { &mut m.extra }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "NostrTag", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for NostrTag { + const NAME: &'static str = "NostrTag"; + + fn is_initialized(&self) -> bool { + if self.key.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.key = ::std::option::Option::Some(is.read_string()?); + }, + 18 => { + self.value = ::std::option::Option::Some(is.read_string()?); + }, + 26 => { + self.extra.push(is.read_string()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.key.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + if let Some(v) = self.value.as_ref() { + my_size += ::protobuf::rt::string_size(2, &v); + } + for value in &self.extra { + my_size += ::protobuf::rt::string_size(3, &value); + }; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.key.as_ref() { + os.write_string(1, v)?; + } + if let Some(v) = self.value.as_ref() { + os.write_string(2, v)?; + } + for v in &self.extra { + os.write_string(3, &v)?; + }; + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> NostrTag { + NostrTag::new() + } + + fn clear(&mut self) { + self.key = ::std::option::Option::None; + self.value = ::std::option::Option::None; + self.extra.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static NostrTag { + static instance: NostrTag = NostrTag { + key: ::std::option::Option::None, + value: ::std::option::Option::None, + extra: ::std::vec::Vec::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for NostrTag { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("NostrTag").unwrap()).clone() + } +} + +impl ::std::fmt::Display for NostrTag { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for NostrTag { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.nostr.NostrSignEvent) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct NostrSignEvent { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrSignEvent.address_n) + pub address_n: ::std::vec::Vec, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrSignEvent.created_at) + pub created_at: ::std::option::Option, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrSignEvent.kind) + pub kind: ::std::option::Option, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrSignEvent.tags) + pub tags: ::std::vec::Vec, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrSignEvent.content) + pub content: ::std::option::Option<::std::string::String>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.nostr.NostrSignEvent.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a NostrSignEvent { + fn default() -> &'a NostrSignEvent { + ::default_instance() + } +} + +impl NostrSignEvent { + pub fn new() -> NostrSignEvent { + ::std::default::Default::default() + } + + // required uint32 created_at = 2; + + pub fn created_at(&self) -> u32 { + self.created_at.unwrap_or(0) + } + + pub fn clear_created_at(&mut self) { + self.created_at = ::std::option::Option::None; + } + + pub fn has_created_at(&self) -> bool { + self.created_at.is_some() + } + + // Param is passed by value, moved + pub fn set_created_at(&mut self, v: u32) { + self.created_at = ::std::option::Option::Some(v); + } + + // required uint32 kind = 3; + + pub fn kind(&self) -> u32 { + self.kind.unwrap_or(0) + } + + pub fn clear_kind(&mut self) { + self.kind = ::std::option::Option::None; + } + + pub fn has_kind(&self) -> bool { + self.kind.is_some() + } + + // Param is passed by value, moved + pub fn set_kind(&mut self, v: u32) { + self.kind = ::std::option::Option::Some(v); + } + + // required string content = 5; + + pub fn content(&self) -> &str { + match self.content.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_content(&mut self) { + self.content = ::std::option::Option::None; + } + + pub fn has_content(&self) -> bool { + self.content.is_some() + } + + // Param is passed by value, moved + pub fn set_content(&mut self, v: ::std::string::String) { + self.content = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_content(&mut self) -> &mut ::std::string::String { + if self.content.is_none() { + self.content = ::std::option::Option::Some(::std::string::String::new()); + } + self.content.as_mut().unwrap() + } + + // Take field + pub fn take_content(&mut self) -> ::std::string::String { + self.content.take().unwrap_or_else(|| ::std::string::String::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(5); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "address_n", + |m: &NostrSignEvent| { &m.address_n }, + |m: &mut NostrSignEvent| { &mut m.address_n }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "created_at", + |m: &NostrSignEvent| { &m.created_at }, + |m: &mut NostrSignEvent| { &mut m.created_at }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "kind", + |m: &NostrSignEvent| { &m.kind }, + |m: &mut NostrSignEvent| { &mut m.kind }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "tags", + |m: &NostrSignEvent| { &m.tags }, + |m: &mut NostrSignEvent| { &mut m.tags }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "content", + |m: &NostrSignEvent| { &m.content }, + |m: &mut NostrSignEvent| { &mut m.content }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "NostrSignEvent", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for NostrSignEvent { + const NAME: &'static str = "NostrSignEvent"; + + fn is_initialized(&self) -> bool { + if self.created_at.is_none() { + return false; + } + if self.kind.is_none() { + return false; + } + if self.content.is_none() { + return false; + } + for v in &self.tags { + if !v.is_initialized() { + return false; + } + }; + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + is.read_repeated_packed_uint32_into(&mut self.address_n)?; + }, + 8 => { + self.address_n.push(is.read_uint32()?); + }, + 16 => { + self.created_at = ::std::option::Option::Some(is.read_uint32()?); + }, + 24 => { + self.kind = ::std::option::Option::Some(is.read_uint32()?); + }, + 34 => { + self.tags.push(is.read_message()?); + }, + 42 => { + self.content = ::std::option::Option::Some(is.read_string()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + for value in &self.address_n { + my_size += ::protobuf::rt::uint32_size(1, *value); + }; + if let Some(v) = self.created_at { + my_size += ::protobuf::rt::uint32_size(2, v); + } + if let Some(v) = self.kind { + my_size += ::protobuf::rt::uint32_size(3, v); + } + for value in &self.tags { + let len = value.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + }; + if let Some(v) = self.content.as_ref() { + my_size += ::protobuf::rt::string_size(5, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + for v in &self.address_n { + os.write_uint32(1, *v)?; + }; + if let Some(v) = self.created_at { + os.write_uint32(2, v)?; + } + if let Some(v) = self.kind { + os.write_uint32(3, v)?; + } + for v in &self.tags { + ::protobuf::rt::write_message_field_with_cached_size(4, v, os)?; + }; + if let Some(v) = self.content.as_ref() { + os.write_string(5, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> NostrSignEvent { + NostrSignEvent::new() + } + + fn clear(&mut self) { + self.address_n.clear(); + self.created_at = ::std::option::Option::None; + self.kind = ::std::option::Option::None; + self.tags.clear(); + self.content = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static NostrSignEvent { + static instance: NostrSignEvent = NostrSignEvent { + address_n: ::std::vec::Vec::new(), + created_at: ::std::option::Option::None, + kind: ::std::option::Option::None, + tags: ::std::vec::Vec::new(), + content: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for NostrSignEvent { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("NostrSignEvent").unwrap()).clone() + } +} + +impl ::std::fmt::Display for NostrSignEvent { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for NostrSignEvent { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.nostr.NostrEventSignature) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct NostrEventSignature { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrEventSignature.pubkey) + pub pubkey: ::std::option::Option<::std::vec::Vec>, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrEventSignature.id) + pub id: ::std::option::Option<::std::vec::Vec>, + // @@protoc_insertion_point(field:hw.trezor.messages.nostr.NostrEventSignature.signature) + pub signature: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.nostr.NostrEventSignature.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a NostrEventSignature { + fn default() -> &'a NostrEventSignature { + ::default_instance() + } +} + +impl NostrEventSignature { + pub fn new() -> NostrEventSignature { + ::std::default::Default::default() + } + + // required bytes pubkey = 1; + + pub fn pubkey(&self) -> &[u8] { + match self.pubkey.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_pubkey(&mut self) { + self.pubkey = ::std::option::Option::None; + } + + pub fn has_pubkey(&self) -> bool { + self.pubkey.is_some() + } + + // Param is passed by value, moved + pub fn set_pubkey(&mut self, v: ::std::vec::Vec) { + self.pubkey = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_pubkey(&mut self) -> &mut ::std::vec::Vec { + if self.pubkey.is_none() { + self.pubkey = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.pubkey.as_mut().unwrap() + } + + // Take field + pub fn take_pubkey(&mut self) -> ::std::vec::Vec { + self.pubkey.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + // required bytes id = 2; + + pub fn id(&self) -> &[u8] { + match self.id.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_id(&mut self) { + self.id = ::std::option::Option::None; + } + + pub fn has_id(&self) -> bool { + self.id.is_some() + } + + // Param is passed by value, moved + pub fn set_id(&mut self, v: ::std::vec::Vec) { + self.id = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_id(&mut self) -> &mut ::std::vec::Vec { + if self.id.is_none() { + self.id = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.id.as_mut().unwrap() + } + + // Take field + pub fn take_id(&mut self) -> ::std::vec::Vec { + self.id.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + // required bytes signature = 3; + + pub fn signature(&self) -> &[u8] { + match self.signature.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_signature(&mut self) { + self.signature = ::std::option::Option::None; + } + + pub fn has_signature(&self) -> bool { + self.signature.is_some() + } + + // Param is passed by value, moved + pub fn set_signature(&mut self, v: ::std::vec::Vec) { + self.signature = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_signature(&mut self) -> &mut ::std::vec::Vec { + if self.signature.is_none() { + self.signature = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.signature.as_mut().unwrap() + } + + // Take field + pub fn take_signature(&mut self) -> ::std::vec::Vec { + self.signature.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "pubkey", + |m: &NostrEventSignature| { &m.pubkey }, + |m: &mut NostrEventSignature| { &mut m.pubkey }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "id", + |m: &NostrEventSignature| { &m.id }, + |m: &mut NostrEventSignature| { &mut m.id }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "signature", + |m: &NostrEventSignature| { &m.signature }, + |m: &mut NostrEventSignature| { &mut m.signature }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "NostrEventSignature", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for NostrEventSignature { + const NAME: &'static str = "NostrEventSignature"; + + fn is_initialized(&self) -> bool { + if self.pubkey.is_none() { + return false; + } + if self.id.is_none() { + return false; + } + if self.signature.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.pubkey = ::std::option::Option::Some(is.read_bytes()?); + }, + 18 => { + self.id = ::std::option::Option::Some(is.read_bytes()?); + }, + 26 => { + self.signature = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.pubkey.as_ref() { + my_size += ::protobuf::rt::bytes_size(1, &v); + } + if let Some(v) = self.id.as_ref() { + my_size += ::protobuf::rt::bytes_size(2, &v); + } + if let Some(v) = self.signature.as_ref() { + my_size += ::protobuf::rt::bytes_size(3, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.pubkey.as_ref() { + os.write_bytes(1, v)?; + } + if let Some(v) = self.id.as_ref() { + os.write_bytes(2, v)?; + } + if let Some(v) = self.signature.as_ref() { + os.write_bytes(3, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> NostrEventSignature { + NostrEventSignature::new() + } + + fn clear(&mut self) { + self.pubkey = ::std::option::Option::None; + self.id = ::std::option::Option::None; + self.signature = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static NostrEventSignature { + static instance: NostrEventSignature = NostrEventSignature { + pubkey: ::std::option::Option::None, + id: ::std::option::Option::None, + signature: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for NostrEventSignature { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("NostrEventSignature").unwrap()).clone() + } +} + +impl ::std::fmt::Display for NostrEventSignature { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for NostrEventSignature { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x14messages-nostr.proto\x12\x18hw.trezor.messages.nostr\"-\n\x0eNostr\ + GetPubkey\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\"%\n\x0bN\ + ostrPubkey\x12\x16\n\x06pubkey\x18\x01\x20\x02(\x0cR\x06pubkey\"H\n\x08N\ + ostrTag\x12\x10\n\x03key\x18\x01\x20\x02(\tR\x03key\x12\x14\n\x05value\ + \x18\x02\x20\x01(\tR\x05value\x12\x14\n\x05extra\x18\x03\x20\x03(\tR\x05\ + extra\"\xb2\x01\n\x0eNostrSignEvent\x12\x1b\n\taddress_n\x18\x01\x20\x03\ + (\rR\x08addressN\x12\x1d\n\ncreated_at\x18\x02\x20\x02(\rR\tcreatedAt\ + \x12\x12\n\x04kind\x18\x03\x20\x02(\rR\x04kind\x126\n\x04tags\x18\x04\ + \x20\x03(\x0b2\".hw.trezor.messages.nostr.NostrTagR\x04tags\x12\x18\n\ + \x07content\x18\x05\x20\x02(\tR\x07content\"[\n\x13NostrEventSignature\ + \x12\x16\n\x06pubkey\x18\x01\x20\x02(\x0cR\x06pubkey\x12\x0e\n\x02id\x18\ + \x02\x20\x02(\x0cR\x02id\x12\x1c\n\tsignature\x18\x03\x20\x02(\x0cR\tsig\ + natureB9\n#com.satoshilabs.trezor.lib.protobufB\x12TrezorMessageNostr\ +"; + +/// `FileDescriptorProto` object which was a source for this generated file +fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); + file_descriptor_proto_lazy.get(|| { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() + }) +} + +/// `FileDescriptor` object which allows dynamic access to files +pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { + static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); + static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); + file_descriptor.get(|| { + let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { + let mut deps = ::std::vec::Vec::with_capacity(0); + let mut messages = ::std::vec::Vec::with_capacity(5); + messages.push(NostrGetPubkey::generated_message_descriptor_data()); + messages.push(NostrPubkey::generated_message_descriptor_data()); + messages.push(NostrTag::generated_message_descriptor_data()); + messages.push(NostrSignEvent::generated_message_descriptor_data()); + messages.push(NostrEventSignature::generated_message_descriptor_data()); + let mut enums = ::std::vec::Vec::with_capacity(0); + ::protobuf::reflect::GeneratedFileDescriptor::new_generated( + file_descriptor_proto(), + deps, + messages, + enums, + ) + }); + ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) + }) +} diff --git a/rust/trezor-client/src/protos/mod.rs b/rust/trezor-client/src/protos/mod.rs index 7dd8483f9c..802423dab5 100644 --- a/rust/trezor-client/src/protos/mod.rs +++ b/rust/trezor-client/src/protos/mod.rs @@ -33,6 +33,7 @@ mod generated { "eos" => messages_eos "monero" => messages_monero "nem" => messages_nem + "nostr" => messages_nostr "ripple" => messages_ripple "solana" => messages_solana "stellar" => messages_stellar diff --git a/tests/device_tests/nostr/__init__.py b/tests/device_tests/nostr/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/device_tests/nostr/test_nostr.py b/tests/device_tests/nostr/test_nostr.py new file mode 100644 index 0000000000..9a8efe6835 --- /dev/null +++ b/tests/device_tests/nostr/test_nostr.py @@ -0,0 +1,129 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2025 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import json +from hashlib import sha256 + +import pytest +from ecdsa import SECP256k1, VerifyingKey +from six import b + +from trezorlib import messages, nostr +from trezorlib.tools import parse_path + +pytestmark = [pytest.mark.altcoin, pytest.mark.models("core")] + +# test data from NIP-06: https://github.com/nostr-protocol/nips/blob/master/06.md + +LEAD_MONKEY_MNEMONIC = ( + "leader monkey parrot ring guide accident before fence cannon height naive bean" +) +LEAD_MONKEY_PUBKEY_HEX = ( + "17162c921dc4d2518f9a101db33695df1afb56ab82f5ff3e5da6eec3ca5cd917" +) + +WHAT_BLEAK_MNEMONIC = "what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade" +WHAT_BLEAK_PUBKEY_HEX = ( + "d41b22899549e1f3d335a31002cfd382174006e166d3e658e3a5eecdb6463573" +) + + +pytestmark_lead_monkey = pytest.mark.setup_client(mnemonic=LEAD_MONKEY_MNEMONIC) +pytestmark_what_bleak = pytest.mark.setup_client(mnemonic=WHAT_BLEAK_MNEMONIC) + +VECTORS = [ + pytest.param(LEAD_MONKEY_PUBKEY_HEX, marks=pytestmark_lead_monkey), + pytest.param(WHAT_BLEAK_PUBKEY_HEX, marks=pytestmark_what_bleak), +] + +TEST_EVENT = { + "created_at": 1737396950, + "kind": 1, + "tags": [ + [ + "e", + "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", + "wss://nostr.example.com", + ], + ["p", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca"], + [ + "a", + "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", + "wss://nostr.example.com", + ], + ["alt", "reply"], + ], + "content": "Hello, world", +} + +SIGN_TEST_EVENT = messages.NostrSignEvent( + address_n=parse_path("m/44h/1237h/0h/0/0"), + created_at=TEST_EVENT["created_at"], + kind=TEST_EVENT["kind"], + content=TEST_EVENT["content"], + tags=[ + messages.NostrTag(key=t[0], value=t[1] if len(t) > 1 else None, extra=t[2:]) + for t in TEST_EVENT["tags"] + ], +) + + +@pytest.mark.parametrize("pubkey_hex", VECTORS) +def test_get_pubkey(client, pubkey_hex): + response = nostr.get_pubkey( + client, + n=parse_path("m/44h/1237h/0h/0/0"), + ) + + assert response == bytes.fromhex(pubkey_hex) + + +@pytest.mark.parametrize("pubkey_hex", VECTORS) +def test_sign_event(client, pubkey_hex): + response = nostr.sign_event(client, SIGN_TEST_EVENT) + + assert response.pubkey == bytes.fromhex(pubkey_hex) + + expected_id = ( + sha256( + json.dumps( + [ + 0, + pubkey_hex, + TEST_EVENT["created_at"], + TEST_EVENT["kind"], + TEST_EVENT["tags"], + TEST_EVENT["content"], + ], + separators=(",", ":"), + ).encode() + ) + .digest() + .hex() + ) + + assert response.id == expected_id + + vk = VerifyingKey.from_string( + b("\x03") + bytes.fromhex(pubkey_hex), + curve=SECP256k1, + # this is a pretty silly way to tell VerifyingKey + # that we do not want the message to be hashed + # when verifying the signature! + hashfunc=lambda x: type("h", (), {"digest": lambda: x}), + ) + + assert vk.verify(bytes.fromhex(response.signature), bytes.fromhex(response.id))