chore: code format

This commit is contained in:
Vlad Stan 2023-03-23 10:35:45 +02:00
parent 0ffb158769
commit 619e0f05e2
20 changed files with 121 additions and 81 deletions

View file

@ -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)

View file

@ -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")

View file

@ -1,6 +1,7 @@
from typing import List, Optional, Union
import shortuuid
from lnbits.helpers import urlsafe_short_hash
from . import db

View file

@ -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

View file

@ -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)

View file

@ -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
@ -34,6 +33,7 @@ class AESCipher(object):
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)

View file

@ -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

View file

@ -7,7 +7,7 @@ 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
@ -23,7 +23,7 @@ class Delegation:
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,

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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())

View file

@ -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

View file

@ -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()}

View file

@ -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] = {}

View file

@ -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__()

View file

@ -75,12 +75,17 @@
</q-tr>
</template>
</q-table>
</q-card-section>
<q-card-section>
<div class="text-weight-bold"> Your endpoint:
<q-badge outline class="q-ml-sm text-subtitle2" color="primary" :label="`wss://${host}/nostrclient/api/v1/relay`" />
</div>
<div class="text-weight-bold">
Your endpoint:
<q-badge
outline
class="q-ml-sm text-subtitle2"
color="primary"
:label="`wss://${host}/nostrclient/api/v1/relay`"
/>
</div>
</q-card-section>
</q-card>
<q-card>
@ -88,7 +93,13 @@
<q-form class="q-gutter-md q-y-md" @submit="addRelay">
<div class="row">
<div class="col q-mx-md q-my-sm">
<q-input outlined v-model="relayToAdd" dense filled label="Relay URL"></q-input>
<q-input
outlined
v-model="relayToAdd"
dense
filled
label="Relay URL"
></q-input>
</div>
<div class="col q-mx-md items-align flex items-center justify-right">
<q-btn unelevated color="primary" type="submit">Add relay </q-btn>
@ -104,17 +115,21 @@
<h6 class="text-subtitle1 q-my-none">Nostrclient Extension</h6>
<p>
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
<p>
<!-- wss://{{host}}nostrclient/api/v1/relay -->
<q-badge outline class="q-ml-sm text-subtitle2" color="primary" :label="`wss://${host}/nostrclient/api/v1/relay`" />
</p>
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
</p>
<p>
<!-- wss://{{host}}nostrclient/api/v1/relay -->
<q-badge
outline
class="q-ml-sm text-subtitle2"
color="primary"
:label="`wss://${host}/nostrclient/api/v1/relay`"
/>
</p>
Only Admin users can manage this extension.
<q-card-section></q-card-section>
</q-card-section>
</q-card>
@ -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)

View file

@ -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

View file

@ -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