diff --git a/Makefile b/Makefile index fb3cbfa8..6c63bf01 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,10 @@ ruff-check: poetry run ruff check . black: - poetry run black . --exclude cashu/nostr + poetry run black . black-check: - poetry run black . --exclude cashu/nostr --check + poetry run black . --check mypy: poetry run mypy cashu --ignore-missing diff --git a/cashu/nostr/bech32.py b/cashu/nostr/bech32.py index b068de77..0ae6c808 100644 --- a/cashu/nostr/bech32.py +++ b/cashu/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/cashu/nostr/client/cbc.py b/cashu/nostr/client/cbc.py index a41dbc07..e69e8b5b 100644 --- a/cashu/nostr/client/cbc.py +++ b/cashu/nostr/client/cbc.py @@ -1,4 +1,3 @@ - from Cryptodome import Random from Cryptodome.Cipher import AES @@ -11,10 +10,10 @@ BLOCK_SIZE = 16 + class AESCipher(object): - """This class is compatible with crypto.createCipheriv('aes-256-cbc') + """This class is compatible with crypto.createCipheriv('aes-256-cbc')""" - """ def __init__(self, key=None): self.key = key @@ -33,9 +32,10 @@ def encrypt(self, plain_text): 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/cashu/nostr/delegation.py b/cashu/nostr/delegation.py index 94801f5c..8b1c3114 100644 --- a/cashu/nostr/delegation.py +++ b/cashu/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/cashu/nostr/event.py b/cashu/nostr/event.py index b903e0ee..0ee7ad0b 100644 --- a/cashu/nostr/event.py +++ b/cashu/nostr/event.py @@ -121,6 +121,7 @@ def __post_init__(self): def id(self) -> str: if self.content is None: raise Exception( - "EncryptedDirectMessage `id` is undefined until its message is encrypted and stored in the `content` field" + "EncryptedDirectMessage `id` is undefined until its message is" + " encrypted and stored in the `content` field" ) return super().id diff --git a/cashu/nostr/message_type.py b/cashu/nostr/message_type.py index 3f5206bd..9f3be785 100644 --- a/cashu/nostr/message_type.py +++ b/cashu/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/cashu/nostr/pow.py b/cashu/nostr/pow.py index e0062884..c1795fcc 100644 --- a/cashu/nostr/pow.py +++ b/cashu/nostr/pow.py @@ -2,6 +2,7 @@ from .event import Event from .key import PrivateKey + def zero_bits(b: int) -> int: n = 0 @@ -14,10 +15,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 +27,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 +48,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/cashu/nostr/relay_manager.py b/cashu/nostr/relay_manager.py index 5b92d8d4..98e1abeb 100644 --- a/cashu/nostr/relay_manager.py +++ b/cashu/nostr/relay_manager.py @@ -64,6 +64,7 @@ def publish_event(self, event: Event): if not event.verify(): raise RelayException( - f"Could not publish {event.id}: failed to verify signature {event.signature}" + f"Could not publish {event.id}: failed to verify signature" + f" {event.signature}" ) self.publish_message(event.to_message()) diff --git a/cashu/nostr/subscription.py b/cashu/nostr/subscription.py index 7afba204..10b53631 100644 --- a/cashu/nostr/subscription.py +++ b/cashu/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()}