mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 10:34:20 +01:00
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:
20
README.md
20
README.md
@@ -119,15 +119,7 @@ MINT_URL=https://8333.space:3338
|
||||
```bash
|
||||
cashu info
|
||||
```
|
||||
|
||||
Returns:
|
||||
```bash
|
||||
Version: 0.14.0
|
||||
Debug: False
|
||||
Cashu dir: /home/user/.cashu
|
||||
Wallet: wallet
|
||||
Mint URL: https://8333.space:3338
|
||||
```
|
||||
This command shows information about your wallet.
|
||||
|
||||
#### Check balance
|
||||
```bash
|
||||
@@ -159,21 +151,11 @@ You should see the encoded token. Copy the token and send it to another user suc
|
||||
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
|
||||
To receive tokens, another user enters:
|
||||
```bash
|
||||
cashu receive cashuAeyJwcm9vZnMiOiBbey...
|
||||
```
|
||||
You should see the balance increase:
|
||||
```bash
|
||||
Balance: 0 sat
|
||||
Balance: 69 sat
|
||||
```
|
||||
|
||||
# Starting the wallet API daemon
|
||||
Nutshell wallet can be used in daemon mode that can be controlled through a REST API:
|
||||
|
||||
@@ -532,6 +532,9 @@ class TokenV3(BaseModel):
|
||||
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":
|
||||
"""
|
||||
@@ -542,6 +545,9 @@ class TokenV3(BaseModel):
|
||||
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)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from pydantic import BaseSettings, Extra, Field
|
||||
|
||||
env = Env()
|
||||
|
||||
VERSION = "0.14.0"
|
||||
VERSION = "0.14.1"
|
||||
|
||||
|
||||
def find_env_file():
|
||||
|
||||
@@ -122,7 +122,7 @@ class NostrClient:
|
||||
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:
|
||||
@@ -143,7 +143,7 @@ class NostrClient:
|
||||
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:
|
||||
|
||||
@@ -107,17 +107,14 @@ def deserialize_token_from_string(token: str) -> TokenV3:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ----- receive token -----
|
||||
|
||||
# deserialize token
|
||||
# dtoken = json.loads(base64.urlsafe_b64decode(token))
|
||||
if token.startswith("cashu"):
|
||||
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(
|
||||
wallet: Wallet,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
@@ -143,8 +156,11 @@ async def receive_nostr(
|
||||
|
||||
# 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")
|
||||
@@ -154,3 +170,4 @@ async def receive_nostr(
|
||||
name="Nostr DM",
|
||||
)
|
||||
t.start()
|
||||
return client
|
||||
|
||||
@@ -94,7 +94,7 @@ def async_set_httpx_client(func):
|
||||
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)
|
||||
|
||||
@@ -198,9 +198,9 @@ class LedgerAPI(object):
|
||||
|
||||
if keyset_id and keyset_id != keyset.id:
|
||||
# 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
|
||||
# ecash is transitioned to 0.14.0.
|
||||
# ecash is transitioned to 0.15.0.
|
||||
logger.debug(
|
||||
f"Keyset ID mismatch: {keyset_id} != {keyset.id}. This can happen due"
|
||||
" to a version upgrade."
|
||||
@@ -732,9 +732,7 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
|
||||
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(
|
||||
@@ -928,7 +926,8 @@ class Wallet(LedgerAPI, WalletP2PK, WalletHTLC, WalletSecrets):
|
||||
):
|
||||
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,
|
||||
|
||||
2
setup.py
2
setup.py
@@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli.cli:cli"]}
|
||||
|
||||
setuptools.setup(
|
||||
name="cashu",
|
||||
version="0.14.0",
|
||||
version="0.14.1",
|
||||
description="Ecash wallet and mint for Bitcoin Lightning",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
Reference in New Issue
Block a user