Skip to content

Commit

Permalink
Merge branch 'main' into wallet/lightning_interface
Browse files Browse the repository at this point in the history
  • Loading branch information
callebtc committed Oct 15, 2023
2 parents d06164d + 3e58670 commit f8dd673
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 32 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 23 additions & 10 deletions cashu/nostr/bech32.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down
10 changes: 5 additions & 5 deletions cashu/nostr/client/cbc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from Cryptodome import Random
from Cryptodome.Cipher import AES

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

Expand All @@ -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)
print(dec_text)
8 changes: 4 additions & 4 deletions cashu/nostr/delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion cashu/nostr/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 7 additions & 2 deletions cashu/nostr/message_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ class ClientMessageType:
REQUEST = "REQ"
CLOSE = "CLOSE"


class RelayMessageType:
EVENT = "EVENT"
NOTICE = "NOTICE"
END_OF_STORED_EVENTS = "EOSE"

@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
return False
10 changes: 8 additions & 2 deletions cashu/nostr/pow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .event import Event
from .key import PrivateKey


def zero_bits(b: int) -> int:
n = 0

Expand All @@ -14,18 +15,22 @@ 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:
break

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)

Expand All @@ -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())
Expand Down
3 changes: 2 additions & 1 deletion cashu/nostr/relay_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
8 changes: 3 additions & 5 deletions cashu/nostr/subscription.py
Original file line number Diff line number Diff line change
@@ -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()}

0 comments on commit f8dd673

Please sign in to comment.