From 619e0f05e25ae46487a06db82ab9d0cec7ba0f20 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 23 Mar 2023 10:35:45 +0200 Subject: [PATCH] chore: code format --- README.md | 2 -- __init__.py | 3 +- crud.py | 1 + models.py | 3 +- nostr/bech32.py | 33 ++++++++++++++------- nostr/client/cbc.py | 12 ++++---- nostr/client/client.py | 18 +++++------ nostr/delegation.py | 8 ++--- nostr/event.py | 7 +++-- nostr/key.py | 13 ++++---- nostr/message_pool.py | 3 +- nostr/message_type.py | 9 ++++-- nostr/pow.py | 11 +++++-- nostr/relay.py | 2 ++ nostr/subscription.py | 8 ++--- services.py | 4 +-- tasks.py | 6 ++-- templates/nostrclient/index.html | 51 +++++++++++++++++++++----------- views.py | 3 +- views_api.py | 5 ++-- 20 files changed, 121 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index e609b6c..5f9bfbc 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,4 @@ `nostrclient` is an always-on extension that can open multiple connections to nostr relays and act as a multiplexer for other clients: You open a single websocket to `nostrclient` which then sends the data to multiple relays. The responses from these relays are then sent back to the client. - - ![2023-03-08 18 11 07](https://user-images.githubusercontent.com/93376500/225265727-369f0f8a-196e-41df-a0d1-98b50a0228be.jpg) diff --git a/__init__.py b/__init__.py index 90da4ec..60d8e23 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,9 @@ from fastapi import APIRouter +from starlette.staticfiles import StaticFiles + from lnbits.db import Database from lnbits.helpers import template_renderer from lnbits.tasks import catch_everything_and_restart -from starlette.staticfiles import StaticFiles db = Database("ext_nostrclient") diff --git a/crud.py b/crud.py index 497fcd7..780642d 100644 --- a/crud.py +++ b/crud.py @@ -1,6 +1,7 @@ from typing import List, Optional, Union import shortuuid + from lnbits.helpers import urlsafe_short_hash from . import db diff --git a/models.py b/models.py index 75a086d..4ed1e30 100644 --- a/models.py +++ b/models.py @@ -3,9 +3,10 @@ from typing import Dict, List, Optional from fastapi import Request from fastapi.param_functions import Query -from lnbits.helpers import urlsafe_short_hash from pydantic import BaseModel, Field +from lnbits.helpers import urlsafe_short_hash + class Relay(BaseModel): id: Optional[str] = None diff --git a/nostr/bech32.py b/nostr/bech32.py index b068de7..0ae6c80 100644 --- a/nostr/bech32.py +++ b/nostr/bech32.py @@ -23,21 +23,25 @@ from enum import Enum + class Encoding(Enum): """Enumeration type to list the various supported encodings.""" + BECH32 = 1 BECH32M = 2 + CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" -BECH32M_CONST = 0x2bc830a3 +BECH32M_CONST = 0x2BC830A3 + def bech32_polymod(values): """Internal function that computes the Bech32 checksum.""" - generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3] chk = 1 for value in values: top = chk >> 25 - chk = (chk & 0x1ffffff) << 5 ^ value + chk = (chk & 0x1FFFFFF) << 5 ^ value for i in range(5): chk ^= generator[i] if ((top >> i) & 1) else 0 return chk @@ -57,6 +61,7 @@ def bech32_verify_checksum(hrp, data): return Encoding.BECH32M return None + def bech32_create_checksum(hrp, data, spec): """Compute the checksum values given HRP and data.""" values = bech32_hrp_expand(hrp) + data @@ -68,26 +73,29 @@ def bech32_create_checksum(hrp, data, spec): def bech32_encode(hrp, data, spec): """Compute a Bech32 string given HRP and data values.""" combined = data + bech32_create_checksum(hrp, data, spec) - return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + return hrp + "1" + "".join([CHARSET[d] for d in combined]) + def bech32_decode(bech): """Validate a Bech32/Bech32m string, and determine HRP and data.""" - if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or - (bech.lower() != bech and bech.upper() != bech)): + if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or ( + bech.lower() != bech and bech.upper() != bech + ): return (None, None, None) bech = bech.lower() - pos = bech.rfind('1') + pos = bech.rfind("1") if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: return (None, None, None) - if not all(x in CHARSET for x in bech[pos+1:]): + if not all(x in CHARSET for x in bech[pos + 1 :]): return (None, None, None) hrp = bech[:pos] - data = [CHARSET.find(x) for x in bech[pos+1:]] + data = [CHARSET.find(x) for x in bech[pos + 1 :]] spec = bech32_verify_checksum(hrp, data) if spec is None: return (None, None, None) return (hrp, data[:-6], spec) + def convertbits(data, frombits, tobits, pad=True): """General power-of-2 base conversion.""" acc = 0 @@ -123,7 +131,12 @@ def decode(hrp, addr): return (None, None) if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: return (None, None) - if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M: + if ( + data[0] == 0 + and spec != Encoding.BECH32 + or data[0] != 0 + and spec != Encoding.BECH32M + ): return (None, None) return (data[0], decoded) diff --git a/nostr/client/cbc.py b/nostr/client/cbc.py index a41dbc0..e69e8b5 100644 --- a/nostr/client/cbc.py +++ b/nostr/client/cbc.py @@ -1,4 +1,3 @@ - from Cryptodome import Random from Cryptodome.Cipher import AES @@ -11,10 +10,10 @@ key = bytes.fromhex("3aa925cb69eb613e2928f8a18279c78b1dca04541dfd064df2eda66b598 BLOCK_SIZE = 16 -class AESCipher(object): - """This class is compatible with crypto.createCipheriv('aes-256-cbc') - """ +class AESCipher(object): + """This class is compatible with crypto.createCipheriv('aes-256-cbc')""" + def __init__(self, key=None): self.key = key @@ -33,9 +32,10 @@ class AESCipher(object): def decrypt(self, iv, enc_text): cipher = AES.new(self.key, AES.MODE_CBC, iv=iv) return self.unpad(cipher.decrypt(enc_text).decode("UTF-8")) - + + if __name__ == "__main__": aes = AESCipher(key=key) iv, enc_text = aes.encrypt(plain_text) dec_text = aes.decrypt(iv, enc_text) - print(dec_text) \ No newline at end of file + print(dec_text) diff --git a/nostr/client/client.py b/nostr/client/client.py index 6fb885f..bf91050 100644 --- a/nostr/client/client.py +++ b/nostr/client/client.py @@ -1,19 +1,15 @@ -from typing import * -import ssl -import time +import base64 import json import os -import base64 - -from ..event import Event -from ..relay_manager import RelayManager -from ..message_type import ClientMessageType -from ..key import PrivateKey, PublicKey +import ssl +import time +from typing import * +from ..event import EncryptedDirectMessage, Event, EventKind from ..filter import Filter, Filters -from ..event import Event, EventKind, EncryptedDirectMessage -from ..relay_manager import RelayManager +from ..key import PrivateKey, PublicKey from ..message_type import ClientMessageType +from ..relay_manager import RelayManager # from aes import AESCipher from . import cbc diff --git a/nostr/delegation.py b/nostr/delegation.py index 94801f5..8b1c311 100644 --- a/nostr/delegation.py +++ b/nostr/delegation.py @@ -7,23 +7,23 @@ class Delegation: delegator_pubkey: str delegatee_pubkey: str event_kind: int - duration_secs: int = 30*24*60 # default to 30 days + duration_secs: int = 30 * 24 * 60 # default to 30 days signature: str = None # set in PrivateKey.sign_delegation @property def expires(self) -> int: return int(time.time()) + self.duration_secs - + @property def conditions(self) -> str: return f"kind={self.event_kind}&created_at<{self.expires}" - + @property def delegation_token(self) -> str: return f"nostr:delegation:{self.delegatee_pubkey}:{self.conditions}" def get_tag(self) -> list[str]: - """ Called by Event """ + """Called by Event""" return [ "delegation", self.delegator_pubkey, diff --git a/nostr/event.py b/nostr/event.py index b903e0e..65b187d 100644 --- a/nostr/event.py +++ b/nostr/event.py @@ -1,10 +1,11 @@ -import time import json +import time from dataclasses import dataclass, field from enum import IntEnum -from typing import List -from secp256k1 import PublicKey from hashlib import sha256 +from typing import List + +from secp256k1 import PublicKey from .message_type import ClientMessageType diff --git a/nostr/key.py b/nostr/key.py index d34697f..8089e11 100644 --- a/nostr/key.py +++ b/nostr/key.py @@ -1,14 +1,15 @@ -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 +import secrets from hashlib import sha256 +import secp256k1 +from cffi import FFI +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + +from . import bech32 from .delegation import Delegation from .event import EncryptedDirectMessage, Event, EventKind -from . import bech32 class PublicKey: diff --git a/nostr/message_pool.py b/nostr/message_pool.py index d364cf2..373d9fc 100644 --- a/nostr/message_pool.py +++ b/nostr/message_pool.py @@ -1,8 +1,9 @@ import json from queue import Queue from threading import Lock -from .message_type import RelayMessageType + from .event import Event +from .message_type import RelayMessageType class EventMessage: diff --git a/nostr/message_type.py b/nostr/message_type.py index 3f5206b..9f3be78 100644 --- a/nostr/message_type.py +++ b/nostr/message_type.py @@ -3,6 +3,7 @@ class ClientMessageType: REQUEST = "REQ" CLOSE = "CLOSE" + class RelayMessageType: EVENT = "EVENT" NOTICE = "NOTICE" @@ -10,6 +11,10 @@ class RelayMessageType: @staticmethod def is_valid(type: str) -> bool: - if type == RelayMessageType.EVENT or type == RelayMessageType.NOTICE or type == RelayMessageType.END_OF_STORED_EVENTS: + if ( + type == RelayMessageType.EVENT + or type == RelayMessageType.NOTICE + or type == RelayMessageType.END_OF_STORED_EVENTS + ): return True - return False \ No newline at end of file + return False diff --git a/nostr/pow.py b/nostr/pow.py index e006288..034ad9a 100644 --- a/nostr/pow.py +++ b/nostr/pow.py @@ -1,7 +1,9 @@ import time + from .event import Event from .key import PrivateKey + def zero_bits(b: int) -> int: n = 0 @@ -14,10 +16,11 @@ def zero_bits(b: int) -> int: return 7 - n + def count_leading_zero_bits(hex_str: str) -> int: total = 0 for i in range(0, len(hex_str) - 2, 2): - bits = zero_bits(int(hex_str[i:i+2], 16)) + bits = zero_bits(int(hex_str[i : i + 2], 16)) total += bits if bits != 8: @@ -25,7 +28,10 @@ def count_leading_zero_bits(hex_str: str) -> int: return total -def mine_event(content: str, difficulty: int, public_key: str, kind: int, tags: list=[]) -> Event: + +def mine_event( + content: str, difficulty: int, public_key: str, kind: int, tags: list = [] +) -> Event: all_tags = [["nonce", "1", str(difficulty)]] all_tags.extend(tags) @@ -43,6 +49,7 @@ def mine_event(content: str, difficulty: int, public_key: str, kind: int, tags: return Event(public_key, content, created_at, kind, all_tags, event_id) + def mine_key(difficulty: int) -> PrivateKey: sk = PrivateKey() num_leading_zero_bits = count_leading_zero_bits(sk.public_key.hex()) diff --git a/nostr/relay.py b/nostr/relay.py index ee78baa..0851c02 100644 --- a/nostr/relay.py +++ b/nostr/relay.py @@ -2,7 +2,9 @@ import json import time from queue import Queue from threading import Lock + from websocket import WebSocketApp + from .event import Event from .filter import Filters from .message_pool import MessagePool diff --git a/nostr/subscription.py b/nostr/subscription.py index 7afba20..10b5363 100644 --- a/nostr/subscription.py +++ b/nostr/subscription.py @@ -1,12 +1,10 @@ from .filter import Filters + class Subscription: - def __init__(self, id: str, filters: Filters=None) -> None: + def __init__(self, id: str, filters: Filters = None) -> None: self.id = id self.filters = filters def to_json_object(self): - return { - "id": self.id, - "filters": self.filters.to_json_array() - } + return {"id": self.id, "filters": self.filters.to_json_array()} diff --git a/services.py b/services.py index 09e9235..ab65dae 100644 --- a/services.py +++ b/services.py @@ -3,16 +3,16 @@ import json from typing import List, Union from fastapi import WebSocket, WebSocketDisconnect + from lnbits.helpers import urlsafe_short_hash -from .nostr.client.client import NostrClient as NostrClientLib from .models import Event, Filter, Filters, Relay, RelayList +from .nostr.client.client import NostrClient as NostrClientLib from .nostr.event import Event as NostrEvent from .nostr.filter import Filter as NostrFilter from .nostr.filter import Filters as NostrFilters from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage - received_subscription_events: dict[str, list[Event]] = {} received_subscription_notices: dict[str, list[NoticeMessage]] = {} received_subscription_eosenotices: dict[str, EndOfStoredEventsMessage] = {} diff --git a/tasks.py b/tasks.py index 7894e40..1566f65 100644 --- a/tasks.py +++ b/tasks.py @@ -2,20 +2,18 @@ import asyncio import ssl import threading +from .crud import get_relays from .nostr.event import Event from .nostr.key import PublicKey from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage from .nostr.relay_manager import RelayManager from .services import ( nostr, - received_subscription_events, received_subscription_eosenotices, + received_subscription_events, ) -from .crud import get_relays - - async def init_relays(): # reinitialize the entire client nostr.__init__() diff --git a/templates/nostrclient/index.html b/templates/nostrclient/index.html index 2a6cf60..b3d58c8 100644 --- a/templates/nostrclient/index.html +++ b/templates/nostrclient/index.html @@ -75,12 +75,17 @@ - -
Your endpoint: - -
+
+ Your endpoint: + +
@@ -88,7 +93,13 @@
- +
Add relay @@ -104,17 +115,21 @@
Nostrclient Extension

This extension is a always-on nostr client that other extensions can - use to send and receive events on nostr. - - Add multiple nostr relays to connect to. The extension then opens a websocket for you to use - at -

- - -

- Only Admin users can manage - this extension. + use to send and receive events on nostr. Add multiple nostr relays to + connect to. The extension then opens a websocket for you to use at

+ +

+ + +

+ Only Admin users can manage this extension. + @@ -217,7 +232,7 @@ message: `Invalid relay URL.`, caption: "Should start with 'wss://'' or 'ws://'" }) - return false; + return false } console.log('ADD RELAY ' + this.relayToAdd) let that = this @@ -229,7 +244,7 @@ {url: this.relayToAdd} ) .then(function (response) { - console.log("response:", response) + console.log('response:', response) if (response.data) { response.data.map(maplrelays) that.nostrrelayLinks = response.data @@ -239,7 +254,7 @@ .catch(function (error) { LNbits.utils.notifyApiError(error) }) - return false; + return false }, deleteRelay(url) { console.log('DELETE RELAY ' + url) diff --git a/views.py b/views.py index ce30b3b..a05f956 100644 --- a/views.py +++ b/views.py @@ -6,11 +6,12 @@ from fastapi import Request from fastapi.param_functions import Query from fastapi.params import Depends from fastapi.templating import Jinja2Templates +from starlette.responses import HTMLResponse + from lnbits.core.crud import update_payment_status from lnbits.core.models import User from lnbits.core.views.api import api_payment from lnbits.decorators import check_admin, check_user_exists -from starlette.responses import HTMLResponse from . import nostr_renderer, nostrclient_ext diff --git a/views_api.py b/views_api.py index c3da200..15cc3ab 100644 --- a/views_api.py +++ b/views_api.py @@ -3,11 +3,12 @@ from http import HTTPStatus from typing import Optional from fastapi import Depends, WebSocket -from lnbits.decorators import check_admin -from lnbits.helpers import urlsafe_short_hash from loguru import logger from starlette.exceptions import HTTPException +from lnbits.decorators import check_admin +from lnbits.helpers import urlsafe_short_hash + from . import nostrclient_ext from .crud import add_relay, delete_relay, get_relays from .models import Relay, RelayList