Skip to content

Commit

Permalink
fix nostr receive
Browse files Browse the repository at this point in the history
  • Loading branch information
callebtc committed Dec 3, 2023
1 parent 74c9317 commit cf559ef
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 38 deletions.
6 changes: 6 additions & 0 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,9 @@ def get_amount(self):
def get_keysets(self):
return list(set([p.id for p in self.get_proofs()]))

def get_mints(self):
return list(set([t.mint for t in self.token if t.mint]))

@classmethod
def deserialize(cls, tokenv3_serialized: str) -> "TokenV3":
"""
Expand All @@ -542,6 +545,9 @@ def deserialize(cls, tokenv3_serialized: str) -> "TokenV3":
f"Token prefix not valid. Expected {prefix}."
)
token_base64 = tokenv3_serialized[len(prefix) :]
# if base64 string is not a multiple of 4, pad it with "="
token_base64 += "=" * (4 - len(token_base64) % 4)

token = json.loads(base64.urlsafe_b64decode(token_base64))
return cls.parse_obj(token)

Expand Down
4 changes: 2 additions & 2 deletions cashu/nostr/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def get_dm(self, sender_publickey: PublicKey, callback_func=None, filter_kwargs=
message = json.dumps(request)
self.relay_manager.publish_message(message)

while True:
while any([r.connected for r in self.relay_manager.relays.values()]):
while self.relay_manager.message_pool.has_events():
event_msg = self.relay_manager.message_pool.get_event()
if "?iv=" in event_msg.event.content:
Expand All @@ -143,7 +143,7 @@ def get_dm(self, sender_publickey: PublicKey, callback_func=None, filter_kwargs=
time.sleep(0.1)

def subscribe(self, callback_func=None):
while True:
while any([r.connected for r in self.relay_manager.relays.values()]):
while self.relay_manager.message_pool.has_events():
event_msg = self.relay_manager.message_pool.get_event()
if callback_func:
Expand Down
15 changes: 6 additions & 9 deletions cashu/wallet/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,13 @@ def deserialize_token_from_string(token: str) -> TokenV3:
except Exception:
pass

# ----- receive token -----
if token.startswith("cashu"):
tokenObj = TokenV3.deserialize(token)
assert len(tokenObj.token), Exception("no proofs in token")
assert len(tokenObj.token[0].proofs), Exception("no proofs in token")
return tokenObj

# deserialize token
# dtoken = json.loads(base64.urlsafe_b64decode(token))
tokenObj = TokenV3.deserialize(token)

# tokenObj = TokenV2.parse_obj(dtoken)
assert len(tokenObj.token), Exception("no proofs in token")
assert len(tokenObj.token[0].proofs), Exception("no proofs in token")
return tokenObj
raise Exception("Invalid token")


async def receive(
Expand Down
36 changes: 18 additions & 18 deletions cashu/wallet/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,24 +139,24 @@ async def m007_nostr(db: Database):
"""
Stores timestamps of nostr operations.
"""
# async with db.connect() as conn:
# await conn.execute("""
# CREATE TABLE IF NOT EXISTS nostr (
# type TEXT NOT NULL,
# last TIMESTAMP DEFAULT NULL
# )
# """)
# await conn.execute(
# """
# INSERT INTO nostr
# (type, last)
# VALUES (?, ?)
# """,
# (
# "dm",
# None,
# ),
# )
async with db.connect() as conn:
await conn.execute("""
CREATE TABLE IF NOT EXISTS nostr (
type TEXT NOT NULL,
last TIMESTAMP DEFAULT NULL
)
""")
await conn.execute(
"""
INSERT INTO nostr
(type, last)
VALUES (?, ?)
""",
(
"dm",
None,
),
)


async def m008_keysets_add_public_keys(db: Database):
Expand Down
27 changes: 22 additions & 5 deletions cashu/wallet/nostr.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import asyncio
import datetime
import threading

import click
from httpx import ConnectError
from loguru import logger

from cashu.core.base import TokenV3

from ..core.settings import settings
from ..nostr.client.client import NostrClient
from ..nostr.event import Event
Expand Down Expand Up @@ -97,7 +100,7 @@ async def send_nostr(

async def receive_nostr(
wallet: Wallet,
):
) -> NostrClient:
if settings.nostr_private_key is None:
print(
"Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in"
Expand All @@ -113,18 +116,28 @@ async def receive_nostr(
await asyncio.sleep(2)

def get_token_callback(event: Event, decrypted_content: str):
date_str = datetime.datetime.fromtimestamp(event.created_at).strftime(
"%Y-%m-%d %H:%M:%S"
)
logger.debug(
f"From {event.public_key[:3]}..{event.public_key[-3:]}: {decrypted_content}"
f"From {event.public_key[:3]}..{event.public_key[-3:]} on {date_str}:"
f" {decrypted_content}"
)
# split the content into words
words = decrypted_content.split(" ")
for w in words:
try:
logger.trace(
f"Nostr: setting last check timestamp to {event.created_at}"
"Nostr: setting last check timestamp to"
f" {event.created_at} ({date_str})"
)
# call the receive method
tokenObj = deserialize_token_from_string(w)
tokenObj: TokenV3 = deserialize_token_from_string(w)
print(
f"Receiving {tokenObj.get_amount()} sat on mint"
f" {tokenObj.get_mints()[0]} from nostr user {event.public_key} at"
f" {date_str}"
)
asyncio.run(
receive(
wallet,
Expand All @@ -143,8 +156,11 @@ def get_token_callback(event: Event, decrypted_content: str):

# determine timestamp of last check so we don't scan all historical DMs
last_check = await get_nostr_last_check_timestamp(db=wallet.db)
logger.debug(f"Last check: {last_check}")
if last_check:
date_str = datetime.datetime.fromtimestamp(last_check).strftime(
"%Y-%m-%d %H:%M:%S"
)
logger.debug(f"Last check: {date_str}")
last_check -= 60 * 60 # 1 hour tolerance

logger.debug("Starting Nostr DM thread")
Expand All @@ -154,3 +170,4 @@ def get_token_callback(event: Event, decrypted_content: str):
name="Nostr DM",
)
t.start()
return client
10 changes: 6 additions & 4 deletions cashu/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def wrapper(self, *args, **kwargs):
proxies=proxies_dict, # type: ignore
headers=headers_dict,
base_url=self.url,
timeout=None if settings.debug else 5,
timeout=5,
)
return await func(self, *args, **kwargs)

Expand Down Expand Up @@ -450,6 +450,9 @@ async def split(
logger.debug("Calling split. POST /split")
split_payload = PostSplitRequest(proofs=proofs, outputs=outputs)

# BEGIN: backwards compatibility pre 0.13.0
split_payload.amount = outputs[0].amount

# construct payload
def _splitrequest_include_fields(proofs: List[Proof]):
"""strips away fields from the model that aren't necessary for the /split"""
Expand Down Expand Up @@ -732,9 +735,7 @@ async def redeem(
proofs (List[Proof]): Proofs to be redeemed.
"""
# verify DLEQ of incoming proofs
logger.debug("Verifying DLEQ of incoming proofs.")
self.verify_proofs_dleq(proofs)
logger.debug("DLEQ verified.")
return await self.split(proofs, sum_proofs(proofs))

async def split(
Expand Down Expand Up @@ -928,7 +929,8 @@ def verify_proofs_dleq(self, proofs: List[Proof]):
):
raise Exception("DLEQ proof invalid.")
else:
logger.debug("DLEQ proof valid.")
logger.trace("DLEQ proof valid.")
logger.debug("Verified incoming DLEQ proofs.")

async def _construct_proofs(
self,
Expand Down

0 comments on commit cf559ef

Please sign in to comment.