Wallet/fix_nostr_timeout (#376)

* fix nostr receive

* split with amount pre 0.13

* drop 0.13.0 amount with split compatibility
This commit is contained in:
callebtc
2023-12-03 01:56:58 +01:00
committed by GitHub
parent 74c9317277
commit 6c8b1a858f
9 changed files with 62 additions and 61 deletions

View File

@@ -119,15 +119,7 @@ MINT_URL=https://8333.space:3338
```bash ```bash
cashu info cashu info
``` ```
This command shows information about your wallet.
Returns:
```bash
Version: 0.14.0
Debug: False
Cashu dir: /home/user/.cashu
Wallet: wallet
Mint URL: https://8333.space:3338
```
#### Check balance #### Check balance
```bash ```bash
@@ -159,21 +151,11 @@ You should see the encoded token. Copy the token and send it to another user suc
cashuAeyJwcm9vZnMiOiBbey... cashuAeyJwcm9vZnMiOiBbey...
``` ```
You can now see that your available balance has dropped by the amount that you reserved for sending if you enter `cashu balance`:
```bash
Balance: 420 sat
```
#### Receive tokens #### Receive tokens
To receive tokens, another user enters: To receive tokens, another user enters:
```bash ```bash
cashu receive cashuAeyJwcm9vZnMiOiBbey... cashu receive cashuAeyJwcm9vZnMiOiBbey...
``` ```
You should see the balance increase:
```bash
Balance: 0 sat
Balance: 69 sat
```
# Starting the wallet API daemon # Starting the wallet API daemon
Nutshell wallet can be used in daemon mode that can be controlled through a REST API: Nutshell wallet can be used in daemon mode that can be controlled through a REST API:

View File

@@ -532,6 +532,9 @@ class TokenV3(BaseModel):
def get_keysets(self): def get_keysets(self):
return list(set([p.id for p in self.get_proofs()])) 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 @classmethod
def deserialize(cls, tokenv3_serialized: str) -> "TokenV3": def deserialize(cls, tokenv3_serialized: str) -> "TokenV3":
""" """
@@ -542,6 +545,9 @@ class TokenV3(BaseModel):
f"Token prefix not valid. Expected {prefix}." f"Token prefix not valid. Expected {prefix}."
) )
token_base64 = tokenv3_serialized[len(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)) token = json.loads(base64.urlsafe_b64decode(token_base64))
return cls.parse_obj(token) return cls.parse_obj(token)

View File

@@ -8,7 +8,7 @@ from pydantic import BaseSettings, Extra, Field
env = Env() env = Env()
VERSION = "0.14.0" VERSION = "0.14.1"
def find_env_file(): def find_env_file():

View File

@@ -122,7 +122,7 @@ class NostrClient:
message = json.dumps(request) message = json.dumps(request)
self.relay_manager.publish_message(message) 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(): while self.relay_manager.message_pool.has_events():
event_msg = self.relay_manager.message_pool.get_event() event_msg = self.relay_manager.message_pool.get_event()
if "?iv=" in event_msg.event.content: if "?iv=" in event_msg.event.content:
@@ -143,7 +143,7 @@ class NostrClient:
time.sleep(0.1) time.sleep(0.1)
def subscribe(self, callback_func=None): 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(): while self.relay_manager.message_pool.has_events():
event_msg = self.relay_manager.message_pool.get_event() event_msg = self.relay_manager.message_pool.get_event()
if callback_func: if callback_func:

View File

@@ -107,17 +107,14 @@ def deserialize_token_from_string(token: str) -> TokenV3:
except Exception: except Exception:
pass pass
# ----- receive token ----- if token.startswith("cashu"):
# deserialize token
# dtoken = json.loads(base64.urlsafe_b64decode(token))
tokenObj = TokenV3.deserialize(token) tokenObj = TokenV3.deserialize(token)
# tokenObj = TokenV2.parse_obj(dtoken)
assert len(tokenObj.token), Exception("no proofs in token") assert len(tokenObj.token), Exception("no proofs in token")
assert len(tokenObj.token[0].proofs), Exception("no proofs in token") assert len(tokenObj.token[0].proofs), Exception("no proofs in token")
return tokenObj return tokenObj
raise Exception("Invalid token")
async def receive( async def receive(
wallet: Wallet, wallet: Wallet,

View File

@@ -139,24 +139,24 @@ async def m007_nostr(db: Database):
""" """
Stores timestamps of nostr operations. Stores timestamps of nostr operations.
""" """
# async with db.connect() as conn: async with db.connect() as conn:
# await conn.execute(""" await conn.execute("""
# CREATE TABLE IF NOT EXISTS nostr ( CREATE TABLE IF NOT EXISTS nostr (
# type TEXT NOT NULL, type TEXT NOT NULL,
# last TIMESTAMP DEFAULT NULL last TIMESTAMP DEFAULT NULL
# ) )
# """) """)
# await conn.execute( await conn.execute(
# """ """
# INSERT INTO nostr INSERT INTO nostr
# (type, last) (type, last)
# VALUES (?, ?) VALUES (?, ?)
# """, """,
# ( (
# "dm", "dm",
# None, None,
# ), ),
# ) )
async def m008_keysets_add_public_keys(db: Database): async def m008_keysets_add_public_keys(db: Database):

View File

@@ -1,10 +1,13 @@
import asyncio import asyncio
import datetime
import threading import threading
import click import click
from httpx import ConnectError from httpx import ConnectError
from loguru import logger from loguru import logger
from cashu.core.base import TokenV3
from ..core.settings import settings from ..core.settings import settings
from ..nostr.client.client import NostrClient from ..nostr.client.client import NostrClient
from ..nostr.event import Event from ..nostr.event import Event
@@ -97,7 +100,7 @@ async def send_nostr(
async def receive_nostr( async def receive_nostr(
wallet: Wallet, wallet: Wallet,
): ) -> NostrClient:
if settings.nostr_private_key is None: if settings.nostr_private_key is None:
print( print(
"Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in" "Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in"
@@ -113,18 +116,28 @@ async def receive_nostr(
await asyncio.sleep(2) await asyncio.sleep(2)
def get_token_callback(event: Event, decrypted_content: str): 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( 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 # split the content into words
words = decrypted_content.split(" ") words = decrypted_content.split(" ")
for w in words: for w in words:
try: try:
logger.trace( 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 # 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( asyncio.run(
receive( receive(
wallet, wallet,
@@ -143,8 +156,11 @@ async def receive_nostr(
# determine timestamp of last check so we don't scan all historical DMs # determine timestamp of last check so we don't scan all historical DMs
last_check = await get_nostr_last_check_timestamp(db=wallet.db) last_check = await get_nostr_last_check_timestamp(db=wallet.db)
logger.debug(f"Last check: {last_check}")
if 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 last_check -= 60 * 60 # 1 hour tolerance
logger.debug("Starting Nostr DM thread") logger.debug("Starting Nostr DM thread")
@@ -154,3 +170,4 @@ async def receive_nostr(
name="Nostr DM", name="Nostr DM",
) )
t.start() t.start()
return client

View File

@@ -94,7 +94,7 @@ def async_set_httpx_client(func):
proxies=proxies_dict, # type: ignore proxies=proxies_dict, # type: ignore
headers=headers_dict, headers=headers_dict,
base_url=self.url, base_url=self.url,
timeout=None if settings.debug else 5, timeout=5,
) )
return await func(self, *args, **kwargs) return await func(self, *args, **kwargs)
@@ -198,9 +198,9 @@ class LedgerAPI(object):
if keyset_id and keyset_id != keyset.id: if keyset_id and keyset_id != keyset.id:
# NOTE: Because of the upcoming change of how to calculate keyset ids # NOTE: Because of the upcoming change of how to calculate keyset ids
# with version 0.14.0, we overwrite the calculated keyset id with the # with version 0.15.0, we overwrite the calculated keyset id with the
# requested one. This is a temporary fix and should be removed once all # requested one. This is a temporary fix and should be removed once all
# ecash is transitioned to 0.14.0. # ecash is transitioned to 0.15.0.
logger.debug( logger.debug(
f"Keyset ID mismatch: {keyset_id} != {keyset.id}. This can happen due" f"Keyset ID mismatch: {keyset_id} != {keyset.id}. This can happen due"
" to a version upgrade." " to a version upgrade."
@@ -732,9 +732,7 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
proofs (List[Proof]): Proofs to be redeemed. proofs (List[Proof]): Proofs to be redeemed.
""" """
# verify DLEQ of incoming proofs # verify DLEQ of incoming proofs
logger.debug("Verifying DLEQ of incoming proofs.")
self.verify_proofs_dleq(proofs) self.verify_proofs_dleq(proofs)
logger.debug("DLEQ verified.")
return await self.split(proofs, sum_proofs(proofs)) return await self.split(proofs, sum_proofs(proofs))
async def split( async def split(
@@ -928,7 +926,8 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
): ):
raise Exception("DLEQ proof invalid.") raise Exception("DLEQ proof invalid.")
else: else:
logger.debug("DLEQ proof valid.") logger.trace("DLEQ proof valid.")
logger.debug("Verified incoming DLEQ proofs.")
async def _construct_proofs( async def _construct_proofs(
self, self,

View File

@@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli.cli:cli"]}
setuptools.setup( setuptools.setup(
name="cashu", name="cashu",
version="0.14.0", version="0.14.1",
description="Ecash wallet and mint for Bitcoin Lightning", description="Ecash wallet and mint for Bitcoin Lightning",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",