mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 10:34:20 +01:00
Nostr-p2nip5 (#110)
* move cli * set_requests decorator * fix wrapper * refactor nostr.py * ignore coroutine unpack error * nostr lib 0.8 * make format
This commit is contained in:
@@ -115,7 +115,7 @@ cashu info
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
```bash
|
```bash
|
||||||
Version: 0.9.2
|
Version: 0.9.3
|
||||||
Debug: False
|
Debug: False
|
||||||
Cashu dir: /home/user/.cashu
|
Cashu dir: /home/user/.cashu
|
||||||
Wallet: wallet
|
Wallet: wallet
|
||||||
|
|||||||
@@ -66,4 +66,4 @@ NOSTR_RELAYS = env.list(
|
|||||||
)
|
)
|
||||||
|
|
||||||
MAX_ORDER = 64
|
MAX_ORDER = 64
|
||||||
VERSION = "0.9.2"
|
VERSION = "0.9.3"
|
||||||
|
|||||||
Submodule cashu/nostr updated: 2872fe3c24...59ddc1b5e0
@@ -1,3 +1,3 @@
|
|||||||
from .cli import cli
|
from .cli.cli import cli
|
||||||
|
|
||||||
cli()
|
cli()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import base64
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
@@ -39,12 +38,9 @@ from cashu.nostr.nostr.client.client import NostrClient
|
|||||||
from cashu.tor.tor import TorProxy
|
from cashu.tor.tor import TorProxy
|
||||||
from cashu.wallet import migrations
|
from cashu.wallet import migrations
|
||||||
from cashu.wallet.crud import (
|
from cashu.wallet.crud import (
|
||||||
get_keyset,
|
|
||||||
get_lightning_invoices,
|
get_lightning_invoices,
|
||||||
get_nostr_last_check_timestamp,
|
|
||||||
get_reserved_proofs,
|
get_reserved_proofs,
|
||||||
get_unused_locks,
|
get_unused_locks,
|
||||||
set_nostr_last_check_timestamp,
|
|
||||||
)
|
)
|
||||||
from cashu.wallet.wallet import Wallet as Wallet
|
from cashu.wallet.wallet import Wallet as Wallet
|
||||||
|
|
||||||
@@ -52,12 +48,11 @@ from .cli_helpers import (
|
|||||||
get_mint_wallet,
|
get_mint_wallet,
|
||||||
print_mint_balances,
|
print_mint_balances,
|
||||||
proofs_to_serialized_tokenv2,
|
proofs_to_serialized_tokenv2,
|
||||||
receive_nostr,
|
|
||||||
redeem_multimint,
|
redeem_multimint,
|
||||||
send_nostr,
|
|
||||||
token_from_lnbits_link,
|
token_from_lnbits_link,
|
||||||
verify_mints,
|
verify_mints,
|
||||||
)
|
)
|
||||||
|
from .nostr import receive_nostr, send_nostr
|
||||||
|
|
||||||
|
|
||||||
async def init_wallet(wallet: Wallet):
|
async def init_wallet(wallet: Wallet):
|
||||||
@@ -144,7 +139,7 @@ async def pay(ctx: Context, invoice: str, yes: bool):
|
|||||||
if wallet.available_balance < amount:
|
if wallet.available_balance < amount:
|
||||||
print("Error: Balance too low.")
|
print("Error: Balance too low.")
|
||||||
return
|
return
|
||||||
_, send_proofs = await wallet.split_to_send(wallet.proofs, amount)
|
_, send_proofs = await wallet.split_to_send(wallet.proofs, amount) # type: ignore
|
||||||
await wallet.pay_lightning(send_proofs, invoice)
|
await wallet.pay_lightning(send_proofs, invoice)
|
||||||
wallet.status()
|
wallet.status()
|
||||||
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
@@ -12,14 +9,7 @@ from loguru import logger
|
|||||||
from cashu.core.base import Proof, TokenV2, TokenV2Mint, WalletKeyset
|
from cashu.core.base import Proof, TokenV2, TokenV2Mint, WalletKeyset
|
||||||
from cashu.core.helpers import sum_proofs
|
from cashu.core.helpers import sum_proofs
|
||||||
from cashu.core.settings import CASHU_DIR, MINT_URL, NOSTR_PRIVATE_KEY, NOSTR_RELAYS
|
from cashu.core.settings import CASHU_DIR, MINT_URL, NOSTR_PRIVATE_KEY, NOSTR_RELAYS
|
||||||
from cashu.nostr.nostr.client.client import NostrClient
|
from cashu.wallet.crud import get_keyset
|
||||||
from cashu.nostr.nostr.event import Event
|
|
||||||
from cashu.nostr.nostr.key import PublicKey
|
|
||||||
from cashu.wallet.crud import (
|
|
||||||
get_keyset,
|
|
||||||
get_nostr_last_check_timestamp,
|
|
||||||
set_nostr_last_check_timestamp,
|
|
||||||
)
|
|
||||||
from cashu.wallet.wallet import Wallet as Wallet
|
from cashu.wallet.wallet import Wallet as Wallet
|
||||||
|
|
||||||
|
|
||||||
@@ -212,81 +202,3 @@ async def proofs_to_serialized_tokenv2(wallet, proofs: List[Proof], url: str):
|
|||||||
token.mints.append(TokenV2Mint(url=url, ids=keysets))
|
token.mints.append(TokenV2Mint(url=url, ids=keysets))
|
||||||
token_serialized = await wallet._serialize_token_base64(token)
|
token_serialized = await wallet._serialize_token_base64(token)
|
||||||
return token_serialized
|
return token_serialized
|
||||||
|
|
||||||
|
|
||||||
async def send_nostr(ctx: Context, amount: int, pubkey: str, verbose: bool, yes: bool):
|
|
||||||
"""
|
|
||||||
Sends tokens via nostr.
|
|
||||||
"""
|
|
||||||
# load a wallet for the chosen mint
|
|
||||||
wallet = await get_mint_wallet(ctx)
|
|
||||||
await wallet.load_proofs()
|
|
||||||
_, send_proofs = await wallet.split_to_send(
|
|
||||||
wallet.proofs, amount, set_reserved=True
|
|
||||||
)
|
|
||||||
token = await wallet.serialize_proofs(send_proofs)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print(token)
|
|
||||||
|
|
||||||
if not yes:
|
|
||||||
print("")
|
|
||||||
click.confirm(
|
|
||||||
f"Send {amount} sat to nostr pubkey {pubkey}?",
|
|
||||||
abort=True,
|
|
||||||
default=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# we only use ephemeral private keys for sending
|
|
||||||
client = NostrClient(relays=NOSTR_RELAYS)
|
|
||||||
if verbose:
|
|
||||||
print(f"Your ephemeral nostr private key: {client.private_key.bech32()}")
|
|
||||||
|
|
||||||
if pubkey.startswith("npub"):
|
|
||||||
pubkey_to = PublicKey().from_npub(pubkey)
|
|
||||||
else:
|
|
||||||
pubkey_to = PublicKey(bytes.fromhex(pubkey))
|
|
||||||
|
|
||||||
client.dm(token, pubkey_to)
|
|
||||||
print(f"Token sent to {pubkey}")
|
|
||||||
client.close()
|
|
||||||
|
|
||||||
|
|
||||||
async def receive_nostr(ctx: Context, verbose: bool):
|
|
||||||
if 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=NOSTR_PRIVATE_KEY, relays=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
|
|
||||||
from cashu.wallet.cli import receive
|
|
||||||
|
|
||||||
asyncio.run(receive(ctx, decrypted_content, ""))
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# determine timestamp of last check so we don't scan all historical DMs
|
|
||||||
wallet: Wallet = ctx.obj["WALLET"]
|
|
||||||
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()
|
|
||||||
125
cashu/wallet/cli/nostr.py
Normal file
125
cashu/wallet/cli/nostr.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
import click
|
||||||
|
from click import Context
|
||||||
|
from requests.exceptions import ConnectionError
|
||||||
|
|
||||||
|
from cashu.core.settings import NOSTR_PRIVATE_KEY, NOSTR_RELAYS
|
||||||
|
from cashu.nostr.nostr.client.client import NostrClient
|
||||||
|
from cashu.nostr.nostr.event import Event
|
||||||
|
from cashu.nostr.nostr.key import PublicKey
|
||||||
|
from cashu.wallet.cli.cli_helpers import get_mint_wallet
|
||||||
|
from cashu.wallet.crud import (
|
||||||
|
get_nostr_last_check_timestamp,
|
||||||
|
set_nostr_last_check_timestamp,
|
||||||
|
)
|
||||||
|
from cashu.wallet.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()
|
||||||
|
# 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(ctx: Context, amount: int, pubkey: str, verbose: bool, yes: bool):
|
||||||
|
"""
|
||||||
|
Sends tokens via nostr.
|
||||||
|
"""
|
||||||
|
# load a wallet for the chosen mint
|
||||||
|
wallet = await get_mint_wallet(ctx)
|
||||||
|
|
||||||
|
if "@" in pubkey:
|
||||||
|
pubkey = await nip5_to_pubkey(wallet, pubkey)
|
||||||
|
|
||||||
|
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=NOSTR_PRIVATE_KEY, relays=NOSTR_RELAYS)
|
||||||
|
if verbose and not 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()}")
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def receive_nostr(ctx: Context, verbose: bool):
|
||||||
|
if 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=NOSTR_PRIVATE_KEY, relays=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
|
||||||
|
from cashu.wallet.cli.cli import receive
|
||||||
|
|
||||||
|
asyncio.run(receive(ctx, decrypted_content, ""))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# determine timestamp of last check so we don't scan all historical DMs
|
||||||
|
wallet: Wallet = ctx.obj["WALLET"]
|
||||||
|
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()
|
||||||
@@ -59,20 +59,17 @@ from cashu.wallet.crud import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LedgerAPI:
|
def async_set_requests(func):
|
||||||
keys: Dict[int, PublicKey]
|
"""
|
||||||
keyset: str
|
Decorator that wraps around any async class method of LedgerAPI that makes
|
||||||
tor: TorProxy
|
API calls. Sets some HTTP headers and starts a Tor instance if none is
|
||||||
db: Database
|
already running and and sets local proxy to use it.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, url):
|
async def wrapper(self, *args, **kwargs):
|
||||||
self.url = url
|
self.s.headers.update({"Client-version": VERSION})
|
||||||
|
|
||||||
def _set_requests(self):
|
|
||||||
s = requests.Session()
|
|
||||||
s.headers.update({"Client-version": VERSION})
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
s.verify = False
|
self.s.verify = False
|
||||||
socks_host, socks_port = None, None
|
socks_host, socks_port = None, None
|
||||||
if TOR and TorProxy().check_platform():
|
if TOR and TorProxy().check_platform():
|
||||||
self.tor = TorProxy(timeout=True)
|
self.tor = TorProxy(timeout=True)
|
||||||
@@ -86,9 +83,28 @@ class LedgerAPI:
|
|||||||
"http": f"socks5://{socks_host}:{socks_port}",
|
"http": f"socks5://{socks_host}:{socks_port}",
|
||||||
"https": f"socks5://{socks_host}:{socks_port}",
|
"https": f"socks5://{socks_host}:{socks_port}",
|
||||||
}
|
}
|
||||||
s.proxies.update(proxies)
|
self.s.proxies.update(proxies)
|
||||||
s.headers.update({"User-Agent": scrts.token_urlsafe(8)})
|
self.s.headers.update({"User-Agent": scrts.token_urlsafe(8)})
|
||||||
return s
|
return await func(self, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class LedgerAPI:
|
||||||
|
keys: Dict[int, PublicKey]
|
||||||
|
keyset: str
|
||||||
|
tor: TorProxy
|
||||||
|
db: Database
|
||||||
|
s: requests.Session
|
||||||
|
|
||||||
|
def __init__(self, url):
|
||||||
|
self.url = url
|
||||||
|
self.s = requests.Session()
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
|
async def _init_s(self):
|
||||||
|
"""Dummy function that can be called from outside to use LedgerAPI.s"""
|
||||||
|
return
|
||||||
|
|
||||||
def _construct_proofs(
|
def _construct_proofs(
|
||||||
self, promises: List[BlindedSignature], secrets: List[str], rs: List[str]
|
self, promises: List[BlindedSignature], secrets: List[str], rs: List[str]
|
||||||
@@ -193,8 +209,8 @@ class LedgerAPI:
|
|||||||
ENDPOINTS
|
ENDPOINTS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def _get_keys(self, url: str):
|
async def _get_keys(self, url: str):
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.get(
|
resp = self.s.get(
|
||||||
url + "/keys",
|
url + "/keys",
|
||||||
)
|
)
|
||||||
@@ -208,11 +224,11 @@ class LedgerAPI:
|
|||||||
keyset = WalletKeyset(public_keys=keyset_keys, mint_url=url)
|
keyset = WalletKeyset(public_keys=keyset_keys, mint_url=url)
|
||||||
return keyset
|
return keyset
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def _get_keyset(self, url: str, keyset_id: str):
|
async def _get_keyset(self, url: str, keyset_id: str):
|
||||||
"""
|
"""
|
||||||
keyset_id is base64, needs to be urlsafe-encoded.
|
keyset_id is base64, needs to be urlsafe-encoded.
|
||||||
"""
|
"""
|
||||||
self.s = self._set_requests()
|
|
||||||
keyset_id_urlsafe = keyset_id.replace("+", "-").replace("/", "_")
|
keyset_id_urlsafe = keyset_id.replace("+", "-").replace("/", "_")
|
||||||
resp = self.s.get(
|
resp = self.s.get(
|
||||||
url + f"/keys/{keyset_id_urlsafe}",
|
url + f"/keys/{keyset_id_urlsafe}",
|
||||||
@@ -227,8 +243,8 @@ class LedgerAPI:
|
|||||||
keyset = WalletKeyset(public_keys=keyset_keys, mint_url=url)
|
keyset = WalletKeyset(public_keys=keyset_keys, mint_url=url)
|
||||||
return keyset
|
return keyset
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def _get_keyset_ids(self, url: str):
|
async def _get_keyset_ids(self, url: str):
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.get(
|
resp = self.s.get(
|
||||||
url + "/keysets",
|
url + "/keysets",
|
||||||
)
|
)
|
||||||
@@ -238,9 +254,9 @@ class LedgerAPI:
|
|||||||
assert len(keysets.keysets), Exception("did not receive any keysets")
|
assert len(keysets.keysets), Exception("did not receive any keysets")
|
||||||
return keysets.dict()
|
return keysets.dict()
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
def request_mint(self, amount):
|
def request_mint(self, amount):
|
||||||
"""Requests a mint from the server and returns Lightning invoice."""
|
"""Requests a mint from the server and returns Lightning invoice."""
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.get(self.url + "/mint", params={"amount": amount})
|
resp = self.s.get(self.url + "/mint", params={"amount": amount})
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return_dict = resp.json()
|
return_dict = resp.json()
|
||||||
@@ -248,13 +264,13 @@ class LedgerAPI:
|
|||||||
mint_response = GetMintResponse.parse_obj(return_dict)
|
mint_response = GetMintResponse.parse_obj(return_dict)
|
||||||
return Invoice(amount=amount, pr=mint_response.pr, hash=mint_response.hash)
|
return Invoice(amount=amount, pr=mint_response.pr, hash=mint_response.hash)
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def mint(self, amounts, payment_hash=None):
|
async def mint(self, amounts, payment_hash=None):
|
||||||
"""Mints new coins and returns a proof of promise."""
|
"""Mints new coins and returns a proof of promise."""
|
||||||
secrets = [self._generate_secret() for s in range(len(amounts))]
|
secrets = [self._generate_secret() for s in range(len(amounts))]
|
||||||
await self._check_used_secrets(secrets)
|
await self._check_used_secrets(secrets)
|
||||||
outputs, rs = self._construct_outputs(amounts, secrets)
|
outputs, rs = self._construct_outputs(amounts, secrets)
|
||||||
outputs_payload = PostMintRequest(outputs=outputs)
|
outputs_payload = PostMintRequest(outputs=outputs)
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.post(
|
resp = self.s.post(
|
||||||
self.url + "/mint",
|
self.url + "/mint",
|
||||||
json=outputs_payload.dict(),
|
json=outputs_payload.dict(),
|
||||||
@@ -271,6 +287,7 @@ class LedgerAPI:
|
|||||||
|
|
||||||
return self._construct_proofs(promises, secrets, rs)
|
return self._construct_proofs(promises, secrets, rs)
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def split(self, proofs, amount, scnd_secret: Optional[str] = None):
|
async def split(self, proofs, amount, scnd_secret: Optional[str] = None):
|
||||||
"""Consume proofs and create new promises based on amount split.
|
"""Consume proofs and create new promises based on amount split.
|
||||||
If scnd_secret is None, random secrets will be generated for the tokens to keep (frst_outputs)
|
If scnd_secret is None, random secrets will be generated for the tokens to keep (frst_outputs)
|
||||||
@@ -315,7 +332,6 @@ class LedgerAPI:
|
|||||||
"proofs": {i: proofs_include for i in range(len(proofs))},
|
"proofs": {i: proofs_include for i in range(len(proofs))},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.post(
|
resp = self.s.post(
|
||||||
self.url + "/split",
|
self.url + "/split",
|
||||||
json=split_payload.dict(include=_splitrequest_include_fields(proofs)), # type: ignore
|
json=split_payload.dict(include=_splitrequest_include_fields(proofs)), # type: ignore
|
||||||
@@ -336,6 +352,7 @@ class LedgerAPI:
|
|||||||
|
|
||||||
return frst_proofs, scnd_proofs
|
return frst_proofs, scnd_proofs
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def check_spendable(self, proofs: List[Proof]):
|
async def check_spendable(self, proofs: List[Proof]):
|
||||||
"""
|
"""
|
||||||
Cheks whether the secrets in proofs are already spent or not and returns a list of booleans.
|
Cheks whether the secrets in proofs are already spent or not and returns a list of booleans.
|
||||||
@@ -348,7 +365,6 @@ class LedgerAPI:
|
|||||||
"proofs": {i: {"secret"} for i in range(len(proofs))},
|
"proofs": {i: {"secret"} for i in range(len(proofs))},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.post(
|
resp = self.s.post(
|
||||||
self.url + "/check",
|
self.url + "/check",
|
||||||
json=payload.dict(include=_check_spendable_include_fields(proofs)), # type: ignore
|
json=payload.dict(include=_check_spendable_include_fields(proofs)), # type: ignore
|
||||||
@@ -359,10 +375,10 @@ class LedgerAPI:
|
|||||||
spendable = CheckSpendableResponse.parse_obj(return_dict)
|
spendable = CheckSpendableResponse.parse_obj(return_dict)
|
||||||
return spendable
|
return spendable
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def check_fees(self, payment_request: str):
|
async def check_fees(self, payment_request: str):
|
||||||
"""Checks whether the Lightning payment is internal."""
|
"""Checks whether the Lightning payment is internal."""
|
||||||
payload = CheckFeesRequest(pr=payment_request)
|
payload = CheckFeesRequest(pr=payment_request)
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.post(
|
resp = self.s.post(
|
||||||
self.url + "/checkfees",
|
self.url + "/checkfees",
|
||||||
json=payload.dict(),
|
json=payload.dict(),
|
||||||
@@ -372,6 +388,7 @@ class LedgerAPI:
|
|||||||
self.raise_on_error(return_dict)
|
self.raise_on_error(return_dict)
|
||||||
return return_dict
|
return return_dict
|
||||||
|
|
||||||
|
@async_set_requests
|
||||||
async def pay_lightning(self, proofs: List[Proof], invoice: str):
|
async def pay_lightning(self, proofs: List[Proof], invoice: str):
|
||||||
"""
|
"""
|
||||||
Accepts proofs and a lightning invoice to pay in exchange.
|
Accepts proofs and a lightning invoice to pay in exchange.
|
||||||
@@ -387,7 +404,6 @@ class LedgerAPI:
|
|||||||
"proofs": {i: proofs_include for i in range(len(proofs))},
|
"proofs": {i: proofs_include for i in range(len(proofs))},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.s = self._set_requests()
|
|
||||||
resp = self.s.post(
|
resp = self.s.post(
|
||||||
self.url + "/melt",
|
self.url + "/melt",
|
||||||
json=payload.dict(include=_meltrequest_include_fields(proofs)), # type: ignore
|
json=payload.dict(include=_meltrequest_include_fields(proofs)), # type: ignore
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "cashu"
|
name = "cashu"
|
||||||
version = "0.9.2"
|
version = "0.9.3"
|
||||||
description = "Ecash wallet and mint."
|
description = "Ecash wallet and mint."
|
||||||
authors = ["calle <callebtc@protonmail.com>"]
|
authors = ["calle <callebtc@protonmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli:cli"]}
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="cashu",
|
name="cashu",
|
||||||
version="0.9.2",
|
version="0.9.3",
|
||||||
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",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from click.testing import CliRunner
|
|||||||
from cashu.core.migrations import migrate_databases
|
from cashu.core.migrations import migrate_databases
|
||||||
from cashu.core.settings import VERSION
|
from cashu.core.settings import VERSION
|
||||||
from cashu.wallet import migrations
|
from cashu.wallet import migrations
|
||||||
from cashu.wallet.cli import cli
|
from cashu.wallet.cli.cli import cli
|
||||||
from cashu.wallet.wallet import Wallet
|
from cashu.wallet.wallet import Wallet
|
||||||
from tests.conftest import SERVER_ENDPOINT, mint
|
from tests.conftest import SERVER_ENDPOINT, mint
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user