-
Notifications
You must be signed in to change notification settings - Fork 88
rollup of many fixes and improvements, windows-compatible #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
651e932
0aac371
a7678af
6ced7d8
af713df
e563b6a
f1356eb
e77c2c5
5cb5933
4755134
0362917
511154f
6aa64fa
b8e7871
b67d5db
9269942
a74aab5
553a316
1162357
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,12 @@ | ||
| import secrets | ||
| import base64 | ||
| import secp256k1 | ||
| from cffi import FFI | ||
| from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | ||
| from cryptography.hazmat.primitives import padding | ||
| from hashlib import sha256 | ||
| from typing import cast, Optional | ||
|
|
||
| import nostr.secp as secp256k1 | ||
|
|
||
| from .delegation import Delegation | ||
| from .event import EncryptedDirectMessage, Event, EventKind | ||
|
|
@@ -35,7 +37,7 @@ def from_npub(cls, npub: str): | |
|
|
||
|
|
||
| class PrivateKey: | ||
| def __init__(self, raw_secret: bytes=None) -> None: | ||
| def __init__(self, raw_secret: Optional[bytes]=None) -> None: | ||
| if not raw_secret is None: | ||
| self.raw_secret = raw_secret | ||
| else: | ||
|
|
@@ -51,17 +53,18 @@ def from_nsec(cls, nsec: str): | |
| raw_secret = bech32.convertbits(data, 5, 8)[:-1] | ||
| return cls(bytes(raw_secret)) | ||
|
|
||
| @classmethod | ||
| def from_hex(cls, hex: str): | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added from_hex |
||
| """ Load a PrivateKey from its bech32/nsec form """ | ||
| return cls(bytes.fromhex(hex)) | ||
|
|
||
| def bech32(self) -> str: | ||
| converted_bits = bech32.convertbits(self.raw_secret, 8, 5) | ||
| return bech32.bech32_encode("nsec", converted_bits, bech32.Encoding.BECH32) | ||
|
|
||
| def hex(self) -> str: | ||
| return self.raw_secret.hex() | ||
|
|
||
| def tweak_add(self, scalar: bytes) -> bytes: | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got rid of this because it's not used, and not tested |
||
| sk = secp256k1.PrivateKey(self.raw_secret) | ||
| return sk.tweak_add(scalar) | ||
|
|
||
| def compute_shared_secret(self, public_key_hex: str) -> bytes: | ||
| pk = secp256k1.PublicKey(bytes.fromhex("02" + public_key_hex), True) | ||
| return pk.ecdh(self.raw_secret, hashfn=copy_x) | ||
|
|
@@ -77,7 +80,7 @@ def encrypt_message(self, message: str, public_key_hex: str) -> str: | |
| encrypted_message = encryptor.update(padded_data) + encryptor.finalize() | ||
|
|
||
| return f"{base64.b64encode(encrypted_message).decode()}?iv={base64.b64encode(iv).decode()}" | ||
|
|
||
| def encrypt_dm(self, dm: EncryptedDirectMessage) -> None: | ||
| dm.content = self.encrypt_message(message=dm.cleartext_content, public_key_hex=dm.recipient_pubkey) | ||
|
|
||
|
|
@@ -104,7 +107,8 @@ def sign_message_hash(self, hash: bytes) -> str: | |
|
|
||
| def sign_event(self, event: Event) -> None: | ||
| if event.kind == EventKind.ENCRYPTED_DIRECT_MESSAGE and event.content is None: | ||
| self.encrypt_dm(event) | ||
| edm = cast(EncryptedDirectMessage, event) | ||
| self.encrypt_dm(edm) | ||
| if event.public_key is None: | ||
| event.public_key = self.public_key.hex() | ||
| event.signature = self.sign_message_hash(bytes.fromhex(event.id)) | ||
|
|
@@ -116,7 +120,7 @@ def __eq__(self, other): | |
| return self.raw_secret == other.raw_secret | ||
|
|
||
|
|
||
| def mine_vanity_key(prefix: str = None, suffix: str = None) -> PrivateKey: | ||
| def mine_vanity_key(prefix: Optional[str] = None, suffix: Optional[str] = None) -> PrivateKey: | ||
| if prefix is None and suffix is None: | ||
| raise ValueError("Expected at least one of 'prefix' or 'suffix' arguments") | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| import json | ||
| import time | ||
| from dataclasses import dataclass | ||
| from queue import Queue | ||
| import logging | ||
|
|
||
| from dataclasses import dataclass, field | ||
| from threading import Lock | ||
| from typing import Optional | ||
| from websocket import WebSocketApp | ||
|
|
@@ -11,6 +13,10 @@ | |
| from .message_type import RelayMessageType | ||
| from .subscription import Subscription | ||
|
|
||
|
|
||
| logger = logging.getLogger('nostr') | ||
|
|
||
|
|
||
| @dataclass | ||
| class RelayPolicy: | ||
| should_read: bool = True | ||
|
|
@@ -34,9 +40,9 @@ class RelayProxyConnectionConfig: | |
| class Relay: | ||
| url: str | ||
| message_pool: MessagePool | ||
| policy: RelayPolicy = RelayPolicy() | ||
| policy: RelayPolicy = field(default_factory=RelayPolicy) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. python 3.11 fix |
||
| proxy_config: Optional[RelayProxyConnectionConfig] = None | ||
| ssl_options: Optional[dict] = None | ||
| proxy_config: RelayProxyConnectionConfig = None | ||
|
|
||
| def __post_init__(self): | ||
| self.queue = Queue() | ||
|
|
@@ -58,7 +64,7 @@ def __post_init__(self): | |
| def connect(self): | ||
| self.ws.run_forever( | ||
| sslopt=self.ssl_options, | ||
| http_proxy_host=self.proxy_config.host if self.proxy_config is not None else None, | ||
| http_proxy_host=self.proxy_config.host if self.proxy_config is not None else None, | ||
| http_proxy_port=self.proxy_config.port if self.proxy_config is not None else None, | ||
| proxy_type=self.proxy_config.type if self.proxy_config is not None else None, | ||
| ) | ||
|
|
@@ -119,17 +125,23 @@ def _on_open(self, class_obj): | |
|
|
||
| def _on_close(self, class_obj, status_code, message): | ||
| self.connected = False | ||
| logger.debug("Relay._on_open: url=%s", self.url) | ||
|
|
||
| def _on_close(self, class_obj, status_code, message): | ||
| logger.debug("Relay._on_close: url=%s, code=%s, message=%s", self.url, | ||
| status_code, message) | ||
|
|
||
| def _on_message(self, class_obj, message: str): | ||
| self.message_pool.add_message(message, self.url) | ||
|
|
||
| def _on_error(self, class_obj, error): | ||
| self.connected = False | ||
| self.error_counter += 1 | ||
| if self.error_threshold and self.error_counter > self.error_threshold: | ||
| pass | ||
| else: | ||
| self.check_reconnect() | ||
| logger.debug("Relay._on_error: url=%s, error=%s", self.url, error) | ||
|
|
||
| def _is_valid_message(self, message: str) -> bool: | ||
| message = message.strip("\n") | ||
|
|
@@ -149,15 +161,7 @@ def _is_valid_message(self, message: str) -> bool: | |
| if subscription_id not in self.subscriptions: | ||
| return False | ||
|
|
||
| e = message_json[2] | ||
| event = Event( | ||
| e["content"], | ||
| e["pubkey"], | ||
| e["created_at"], | ||
| e["kind"], | ||
| e["tags"], | ||
| e["sig"], | ||
| ) | ||
| event = Event.from_dict(message_json[2]) | ||
| if not event.verify(): | ||
| return False | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useful!