mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-01-06 10:24:21 +01:00
* Add REST API for Cashu wallet * Add simple way to start REST API server * Add simple tests for wallet REST API * Add TokenV3 to REST API * Improve tests for wallet REST API * Make format * Remove unused import * Rename nostr module for API * Unify helper functions for CLI and API * Make format * Move error handling from helper functions to API * Remove 'is_api' flag where possible * Make format, cleanup, add comments * Fix typo for burn also in API * Improve error handling for API * Add flag 'trust_new_mint' to receive command To enable trusting or mistrusting unknown mints via API * Allow selecting mint for sending from API * Fix: set specific mint via API * Fix: select mint with maximum balance via CLI * Use different variables for mint_nr * Allow selecting mint when sending via nostr via API * Remove unnessecary 'is_api' flags from 'send_nostr' * Remove HTTPException from nostr.py * Allow selecting mint for sending with parameter also via CLI * Allow trusting unknown mint for receiving also via CLI * Make format * Enable trusting unknown mint also when receiving via nostr * Fix: wrong indentation of in receive function * Use relative imports for wallet API * Unify get_mint_wallet for CLI and API * Unify send command for CLI and API * Unify receive for CLI and API * Catch errors in nostr via API * Remove flag 'is_api' from verify_mints_tokenv2 * Remove cli_helpers left from last merge * refactor cli selection * load mint in nostr_send * cleanup * add cli_helpers.py * legacy deserialization in cli * make format * clean up api response * fix tests * try pk * verify mints in api * try github tests * Fix: verify_mints for API * Uncomment verify_mints in receive of API * update README * Show mint url in pending * clean up balance response * fix test * mint selection in api * clean up API * CLI: verify mint for receive -a * clean up * Rest -> REST * Remove unused imports --------- Co-authored-by: sihamon <sihamon@proton.me> Co-authored-by: sihamon <126967444+sihamon@users.noreply.github.com>
145 lines
4.3 KiB
Python
145 lines
4.3 KiB
Python
import asyncio
|
|
import threading
|
|
import time
|
|
|
|
import click
|
|
from requests.exceptions import ConnectionError
|
|
|
|
from ..core.settings import settings
|
|
from ..nostr.nostr.client.client import NostrClient
|
|
from ..nostr.nostr.event import Event
|
|
from ..nostr.nostr.key import PublicKey
|
|
from .cli.cli_helpers import get_mint_wallet
|
|
from .crud import get_nostr_last_check_timestamp, set_nostr_last_check_timestamp
|
|
from .helpers import receive
|
|
from .wallet import Wallet
|
|
|
|
|
|
async def nip5_to_pubkey(wallet: Wallet, address: str):
|
|
"""
|
|
Retrieves the nostr public key of a NIP-05 identifier.
|
|
"""
|
|
# we will be using the requests session from the wallet
|
|
await wallet._init_s()
|
|
# if no username is given, use default _ (NIP-05 stuff)
|
|
if "@" not in address:
|
|
address = "_@" + address
|
|
# now we can use it
|
|
user, host = address.split("@")
|
|
resp_dict = {}
|
|
try:
|
|
resp = wallet.s.get(
|
|
f"https://{host}/.well-known/nostr.json?name={user}",
|
|
)
|
|
resp.raise_for_status()
|
|
except ConnectionError:
|
|
raise Exception(f"Could not connect to {host}")
|
|
except Exception as e:
|
|
raise e
|
|
resp_dict = resp.json()
|
|
assert "names" in resp_dict, Exception(f"did not receive any names from {host}")
|
|
assert user in resp_dict["names"], Exception(f"{user}@{host} not found")
|
|
pubkey = resp_dict["names"][user]
|
|
return pubkey
|
|
|
|
|
|
async def send_nostr(
|
|
wallet: Wallet,
|
|
amount: int,
|
|
pubkey: str,
|
|
verbose: bool = False,
|
|
yes: bool = True,
|
|
):
|
|
"""
|
|
Sends tokens via nostr.
|
|
"""
|
|
|
|
if "@" in pubkey or "." in pubkey:
|
|
# matches user@domain.com and domain.com (which is _@domain.com)
|
|
pubkey = await nip5_to_pubkey(wallet, pubkey)
|
|
await wallet.load_mint()
|
|
await wallet.load_proofs()
|
|
_, send_proofs = await wallet.split_to_send(
|
|
wallet.proofs, amount, set_reserved=True
|
|
)
|
|
token = await wallet.serialize_proofs(send_proofs)
|
|
|
|
if pubkey.startswith("npub"):
|
|
pubkey_to = PublicKey().from_npub(pubkey)
|
|
else:
|
|
pubkey_to = PublicKey(bytes.fromhex(pubkey))
|
|
|
|
print("")
|
|
print(token)
|
|
|
|
if not yes:
|
|
print("")
|
|
click.confirm(
|
|
f"Send {amount} sat to {pubkey_to.bech32()}?",
|
|
abort=True,
|
|
default=True,
|
|
)
|
|
|
|
client = NostrClient(
|
|
private_key=settings.nostr_private_key or "", relays=settings.nostr_relays
|
|
)
|
|
if verbose and not settings.nostr_private_key:
|
|
# we generated a random key if none was present
|
|
print(f"Your nostr private key: {client.private_key.bech32()}")
|
|
|
|
client.dm(token, pubkey_to)
|
|
print(f"Token sent to {pubkey_to.bech32()}")
|
|
await asyncio.sleep(5)
|
|
client.close()
|
|
return token, pubkey_to.bech32()
|
|
|
|
|
|
async def receive_nostr(
|
|
wallet: Wallet,
|
|
verbose: bool = False,
|
|
):
|
|
if settings.nostr_private_key is None:
|
|
print(
|
|
"Warning: No nostr private key set! You don't have NOSTR_PRIVATE_KEY set in your .env file. "
|
|
"I will create a random private key for this session but I will not remember it."
|
|
)
|
|
print("")
|
|
client = NostrClient(
|
|
private_key=settings.nostr_private_key, relays=settings.nostr_relays
|
|
)
|
|
print(f"Your nostr public key: {client.public_key.bech32()}")
|
|
if verbose:
|
|
print(f"Your nostr private key (do not share!): {client.private_key.bech32()}")
|
|
await asyncio.sleep(2)
|
|
|
|
def get_token_callback(event: Event, decrypted_content):
|
|
if verbose:
|
|
print(
|
|
f"From {event.public_key[:3]}..{event.public_key[-3:]}: {decrypted_content}"
|
|
)
|
|
try:
|
|
# call the receive method
|
|
|
|
asyncio.run(
|
|
receive(
|
|
wallet,
|
|
decrypted_content,
|
|
"",
|
|
)
|
|
)
|
|
except Exception as e:
|
|
pass
|
|
|
|
# determine timestamp of last check so we don't scan all historical DMs
|
|
last_check = await get_nostr_last_check_timestamp(db=wallet.db)
|
|
if last_check:
|
|
last_check -= 60 * 60 # 1 hour tolerance
|
|
await set_nostr_last_check_timestamp(timestamp=int(time.time()), db=wallet.db)
|
|
|
|
t = threading.Thread(
|
|
target=client.get_dm,
|
|
args=(client.public_key, get_token_callback, {"since": last_check}),
|
|
name="Nostr DM",
|
|
)
|
|
t.start()
|