mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-23 11:44:19 +01:00
@@ -24,8 +24,8 @@ MINT_PRIVATE_KEY=supersecretprivatekey
|
||||
# Supported: LNbitsWallet, FakeWallet
|
||||
MINT_LIGHTNING_BACKEND=LNbitsWallet
|
||||
|
||||
MINT_SERVER_HOST=127.0.0.1
|
||||
MINT_SERVER_PORT=3338
|
||||
MINT_LISTEN_HOST=127.0.0.1
|
||||
MINT_LISTEN_PORT=3338
|
||||
|
||||
LIGHTNING=TRUE
|
||||
# fee to reserve in percent of the amount
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -31,8 +31,8 @@ jobs:
|
||||
env:
|
||||
LIGHTNING: False
|
||||
MINT_PRIVATE_KEY: "testingkey"
|
||||
MINT_SERVER_HOST: 0.0.0.0
|
||||
MINT_SERVER_PORT: 3337
|
||||
MINT_LISTEN_HOST: 0.0.0.0
|
||||
MINT_LISTEN_PORT: 3337
|
||||
run: |
|
||||
nohup poetry run mint &
|
||||
- name: Run tests
|
||||
|
||||
@@ -3,7 +3,7 @@ import hashlib
|
||||
from typing import Dict, List
|
||||
|
||||
from cashu.core.secp import PrivateKey, PublicKey
|
||||
from cashu.core.settings import MAX_ORDER
|
||||
from cashu.core.settings import settings
|
||||
|
||||
# entropy = bytes([random.getrandbits(8) for i in range(16)])
|
||||
# mnemonic = bip39.mnemonic_from_bytes(entropy)
|
||||
@@ -27,12 +27,14 @@ def derive_keys(master_key: str, derivation_path: str = ""):
|
||||
.encode("utf-8")[:32],
|
||||
raw=True,
|
||||
)
|
||||
for i in range(MAX_ORDER)
|
||||
for i in range(settings.max_order)
|
||||
}
|
||||
|
||||
|
||||
def derive_pubkeys(keys: Dict[int, PrivateKey]):
|
||||
return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
|
||||
return {
|
||||
amt: keys[amt].pubkey for amt in [2**i for i in range(settings.max_order)]
|
||||
}
|
||||
|
||||
|
||||
def derive_keyset_id(keys: Dict[int, PublicKey]):
|
||||
|
||||
@@ -3,7 +3,7 @@ from functools import partial, wraps
|
||||
from typing import List
|
||||
|
||||
from cashu.core.base import Proof
|
||||
from cashu.core.settings import LIGHTNING_FEE_PERCENT, LIGHTNING_RESERVE_FEE_MIN
|
||||
from cashu.core.settings import settings
|
||||
|
||||
|
||||
def sum_proofs(proofs: List[Proof]):
|
||||
@@ -39,5 +39,6 @@ def fee_reserve(amount_msat: int, internal=False) -> int:
|
||||
if internal:
|
||||
return 0
|
||||
return max(
|
||||
int(LIGHTNING_RESERVE_FEE_MIN), int(amount_msat * LIGHTNING_FEE_PERCENT / 100.0)
|
||||
int(settings.lightning_reserve_fee_min),
|
||||
int(amount_msat * settings.lightning_fee_percent / 100.0),
|
||||
)
|
||||
|
||||
@@ -1,72 +1,112 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
from typing import List, Union
|
||||
|
||||
from environs import Env # type: ignore
|
||||
from pydantic import BaseSettings, Extra, Field, validator
|
||||
|
||||
env = Env()
|
||||
|
||||
# env file: default to current dir, else home dir
|
||||
ENV_FILE = os.path.join(os.getcwd(), ".env")
|
||||
if not os.path.isfile(ENV_FILE):
|
||||
ENV_FILE = os.path.join(str(Path.home()), ".cashu", ".env")
|
||||
if os.path.isfile(ENV_FILE):
|
||||
env.read_env(ENV_FILE)
|
||||
else:
|
||||
ENV_FILE = ""
|
||||
env.read_env(recurse=False)
|
||||
|
||||
DEBUG = env.bool("DEBUG", default=False)
|
||||
if not DEBUG:
|
||||
sys.tracebacklimit = 0
|
||||
|
||||
CASHU_DIR = env.str("CASHU_DIR", default=os.path.join(str(Path.home()), ".cashu"))
|
||||
CASHU_DIR = CASHU_DIR.replace("~", str(Path.home()))
|
||||
assert len(CASHU_DIR), "CASHU_DIR not defined"
|
||||
|
||||
TOR = env.bool("TOR", default=True)
|
||||
|
||||
SOCKS_HOST = env.str("SOCKS_HOST", default=None)
|
||||
SOCKS_PORT = env.int("SOCKS_PORT", default=9050)
|
||||
|
||||
LIGHTNING = env.bool("LIGHTNING", default=True)
|
||||
LIGHTNING_FEE_PERCENT = env.float("LIGHTNING_FEE_PERCENT", default=1.0)
|
||||
assert LIGHTNING_FEE_PERCENT >= 0, "LIGHTNING_FEE_PERCENT must be at least 0"
|
||||
LIGHTNING_RESERVE_FEE_MIN = env.float("LIGHTNING_RESERVE_FEE_MIN", default=2000)
|
||||
|
||||
MINT_PRIVATE_KEY = env.str("MINT_PRIVATE_KEY", default=None)
|
||||
|
||||
MINT_SERVER_HOST = env.str("MINT_SERVER_HOST", default="127.0.0.1")
|
||||
MINT_SERVER_PORT = env.int("MINT_SERVER_PORT", default=3338)
|
||||
|
||||
MINT_URL = env.str("MINT_URL", default=None)
|
||||
MINT_HOST = env.str("MINT_HOST", default="8333.space")
|
||||
MINT_PORT = env.int("MINT_PORT", default=3338)
|
||||
|
||||
MINT_LIGHTNING_BACKEND = env.str("MINT_LIGHTNING_BACKEND", default="FakeWallet")
|
||||
MINT_DATABASE = env.str("MINT_DATABASE", default="data/mint")
|
||||
|
||||
if not MINT_URL:
|
||||
if MINT_HOST in ["localhost", "127.0.0.1"]:
|
||||
MINT_URL = f"http://{MINT_HOST}:{MINT_PORT}"
|
||||
else:
|
||||
MINT_URL = f"https://{MINT_HOST}:{MINT_PORT}"
|
||||
|
||||
LNBITS_ENDPOINT = env.str("LNBITS_ENDPOINT", default=None)
|
||||
LNBITS_KEY = env.str("LNBITS_KEY", default=None)
|
||||
|
||||
NOSTR_PRIVATE_KEY = env.str("NOSTR_PRIVATE_KEY", default=None)
|
||||
NOSTR_RELAYS = env.list(
|
||||
"NOSTR_RELAYS",
|
||||
default=[
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.damus.io",
|
||||
"wss://nostr.zebedee.cloud",
|
||||
"wss://relay.snort.social",
|
||||
"wss://nostr.fmt.wiz.biz",
|
||||
],
|
||||
)
|
||||
|
||||
MAX_ORDER = 64
|
||||
VERSION = "0.9.4"
|
||||
|
||||
|
||||
def find_env_file():
|
||||
# env file: default to current dir, else home dir
|
||||
ENV_FILE = os.path.join(os.getcwd(), ".env")
|
||||
if not os.path.isfile(ENV_FILE):
|
||||
ENV_FILE = os.path.join(str(Path.home()), ".cashu", ".env")
|
||||
if os.path.isfile(ENV_FILE):
|
||||
env.read_env(ENV_FILE)
|
||||
else:
|
||||
ENV_FILE = ""
|
||||
return ENV_FILE
|
||||
|
||||
|
||||
class CashuSettings(BaseSettings):
|
||||
env_file: str = Field(default=None)
|
||||
lightning: bool = Field(default=True)
|
||||
lightning_fee_percent: float = Field(default=1.0)
|
||||
lightning_reserve_fee_min: int = Field(default=2000)
|
||||
max_order: int = Field(default=64)
|
||||
|
||||
class Config:
|
||||
env_file = find_env_file()
|
||||
env_file_encoding = "utf-8"
|
||||
case_sensitive = False
|
||||
extra = Extra.ignore
|
||||
|
||||
# def __init__(self, env_file=None):
|
||||
# self.env_file = env_file or self.env_file
|
||||
|
||||
|
||||
class EnvSettings(CashuSettings):
|
||||
debug: bool = Field(default=False)
|
||||
host: str = Field(default="127.0.0.1")
|
||||
port: int = Field(default=5000)
|
||||
cashu_dir: str = Field(default=os.path.join(str(Path.home()), ".cashu"))
|
||||
|
||||
|
||||
class MintSettings(CashuSettings):
|
||||
mint_private_key: str = Field(default=None)
|
||||
mint_listen_host: str = Field(default="127.0.0.1")
|
||||
mint_listen_port: int = Field(default=3338)
|
||||
mint_lightning_backend: str = Field(default="LNbitsWallet")
|
||||
mint_database: str = Field(default="data/mint")
|
||||
|
||||
mint_lnbits_endpoint: str = Field(default=None)
|
||||
mint_lnbits_key: str = Field(default=None)
|
||||
|
||||
|
||||
class WalletSettings(CashuSettings):
|
||||
lightning: bool = Field(default=True)
|
||||
tor: bool = Field(default=True)
|
||||
socks_host: str = Field(default=None)
|
||||
socks_port: int = Field(default=9050)
|
||||
mint_url: str = Field(default=None)
|
||||
mint_host: str = Field(default="8333.space")
|
||||
mint_port: int = Field(default=3338)
|
||||
|
||||
nostr_private_key: str = Field(default=None)
|
||||
nostr_relays: List[str] = Field(
|
||||
default=[
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.damus.io",
|
||||
"wss://nostr.zebedee.cloud",
|
||||
"wss://relay.snort.social",
|
||||
"wss://nostr.fmt.wiz.biz",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class Settings(EnvSettings, MintSettings, WalletSettings, CashuSettings):
|
||||
version: str = Field(default=VERSION)
|
||||
|
||||
# def __init__(self, env_file=None):
|
||||
# super().Config(env_file=env_file)
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
||||
def startup_settings_tasks():
|
||||
# set env_file (this does not affect the settings module, it's just for reading)
|
||||
settings.env_file = find_env_file()
|
||||
|
||||
if not settings.debug:
|
||||
# set traceback limit
|
||||
sys.tracebacklimit = 0
|
||||
|
||||
# replace ~ with home directory in cashu_dir
|
||||
settings.cashu_dir = settings.cashu_dir.replace("~", str(Path.home()))
|
||||
|
||||
# set mint_url if only mint_host is set
|
||||
if not settings.mint_url:
|
||||
if settings.mint_host in ["localhost", "127.0.0.1"] and settings.mint_port:
|
||||
# localhost without https
|
||||
settings.mint_url = f"http://{settings.mint_host}:{settings.mint_port}"
|
||||
else:
|
||||
settings.mint_url = f"https://{settings.mint_host}:{settings.mint_port}"
|
||||
|
||||
|
||||
startup_settings_tasks()
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Dict, Optional
|
||||
|
||||
import requests
|
||||
|
||||
from cashu.core.settings import DEBUG, LNBITS_ENDPOINT, LNBITS_KEY
|
||||
from cashu.core.settings import settings
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
@@ -18,14 +18,14 @@ class LNbitsWallet(Wallet):
|
||||
"""https://github.com/lnbits/lnbits"""
|
||||
|
||||
def __init__(self):
|
||||
self.endpoint = LNBITS_ENDPOINT
|
||||
self.endpoint = settings.mint_lnbits_endpoint
|
||||
|
||||
key = LNBITS_KEY
|
||||
key = settings.mint_lnbits_key
|
||||
self.key = {"X-Api-Key": key}
|
||||
self.s = requests.Session()
|
||||
self.s.auth = ("user", "pass")
|
||||
self.s.headers.update({"X-Api-Key": key})
|
||||
self.s.verify = not DEBUG
|
||||
self.s.verify = not settings.debug
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
try:
|
||||
|
||||
@@ -7,7 +7,7 @@ from starlette.middleware import Middleware
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
from cashu.core.settings import DEBUG, VERSION
|
||||
from cashu.core.settings import settings
|
||||
|
||||
from .router import router
|
||||
from .startup import start_mint_init
|
||||
@@ -33,7 +33,7 @@ def create_app(config_object="core.settings") -> FastAPI:
|
||||
def __init__(self):
|
||||
self.padding = 0
|
||||
self.minimal_fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>\n"
|
||||
if DEBUG:
|
||||
if settings.debug:
|
||||
self.fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <4}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>\n"
|
||||
else:
|
||||
self.fmt: str = self.minimal_fmt
|
||||
@@ -53,7 +53,7 @@ def create_app(config_object="core.settings") -> FastAPI:
|
||||
logger.log(level, record.getMessage())
|
||||
|
||||
logger.remove()
|
||||
log_level: str = "DEBUG" if DEBUG else "INFO"
|
||||
log_level: str = "DEBUG" if settings.debug else "INFO"
|
||||
formatter = Formatter()
|
||||
logger.add(sys.stderr, level=log_level, format=formatter.format)
|
||||
|
||||
@@ -82,7 +82,7 @@ def create_app(config_object="core.settings") -> FastAPI:
|
||||
app = FastAPI(
|
||||
title="Cashu Python Mint",
|
||||
description="Ecash wallet and mint for Bitcoin",
|
||||
version=VERSION,
|
||||
version=settings.version,
|
||||
license_info={
|
||||
"name": "MIT License",
|
||||
"url": "https://raw.githubusercontent.com/cashubtc/cashu/main/LICENSE",
|
||||
|
||||
@@ -18,7 +18,7 @@ from cashu.core.db import Database
|
||||
from cashu.core.helpers import fee_reserve, sum_proofs
|
||||
from cashu.core.script import verify_script
|
||||
from cashu.core.secp import PublicKey
|
||||
from cashu.core.settings import LIGHTNING, MAX_ORDER, VERSION
|
||||
from cashu.core.settings import settings
|
||||
from cashu.core.split import amount_split
|
||||
from cashu.lightning.base import Wallet
|
||||
from cashu.mint.crud import LedgerCrud
|
||||
@@ -51,7 +51,9 @@ class Ledger:
|
||||
async def load_keyset(self, derivation_path, autosave=True):
|
||||
"""Load current keyset keyset or generate new one."""
|
||||
keyset = MintKeyset(
|
||||
seed=self.master_key, derivation_path=derivation_path, version=VERSION
|
||||
seed=self.master_key,
|
||||
derivation_path=derivation_path,
|
||||
version=settings.version,
|
||||
)
|
||||
# check if current keyset is stored in db and store if not
|
||||
logger.trace(f"Loading keyset {keyset.id} from db.")
|
||||
@@ -192,7 +194,9 @@ class Ledger:
|
||||
|
||||
def _verify_amount(self, amount: int):
|
||||
"""Any amount used should be a positive integer not larger than 2^MAX_ORDER."""
|
||||
valid = isinstance(amount, int) and amount > 0 and amount < 2**MAX_ORDER
|
||||
valid = (
|
||||
isinstance(amount, int) and amount > 0 and amount < 2**settings.max_order
|
||||
)
|
||||
if not valid:
|
||||
raise Exception("invalid amount: " + str(amount))
|
||||
return amount
|
||||
@@ -359,7 +363,7 @@ class Ledger:
|
||||
amounts = [b.amount for b in B_s]
|
||||
amount = sum(amounts)
|
||||
# check if lightning invoice was paid
|
||||
if LIGHTNING:
|
||||
if settings.lightning:
|
||||
if not payment_hash:
|
||||
raise Exception("no payment_hash provided.")
|
||||
try:
|
||||
@@ -368,8 +372,10 @@ class Ledger:
|
||||
raise e
|
||||
|
||||
for amount in amounts:
|
||||
if amount not in [2**i for i in range(MAX_ORDER)]:
|
||||
raise Exception(f"Can only mint amounts with 2^n up to {2**MAX_ORDER}.")
|
||||
if amount not in [2**i for i in range(settings.max_order)]:
|
||||
raise Exception(
|
||||
f"Can only mint amounts with 2^n up to {2**settings.max_order}."
|
||||
)
|
||||
|
||||
promises = await self._generate_promises(B_s, keyset)
|
||||
return promises
|
||||
@@ -391,7 +397,7 @@ class Ledger:
|
||||
"provided proofs not enough for Lightning payment."
|
||||
)
|
||||
|
||||
if LIGHTNING:
|
||||
if settings.lightning:
|
||||
status, preimage = await self._pay_lightning_invoice(invoice, fees_msat)
|
||||
else:
|
||||
status, preimage = True, "preimage"
|
||||
@@ -413,7 +419,7 @@ class Ledger:
|
||||
"""Returns the fees (in msat) required to pay this pr."""
|
||||
# hack: check if it's internal, if it exists, it will return paid = False,
|
||||
# if id does not exist (not internal), it returns paid = None
|
||||
if LIGHTNING:
|
||||
if settings.lightning:
|
||||
decoded_invoice = bolt11.decode(pr)
|
||||
amount = math.ceil(decoded_invoice.amount_msat / 1000)
|
||||
paid = await self.lightning.get_invoice_status(decoded_invoice.payment_hash)
|
||||
|
||||
@@ -4,7 +4,7 @@ import click
|
||||
import uvicorn
|
||||
from click import Context
|
||||
|
||||
from cashu.core.settings import MINT_SERVER_HOST, MINT_SERVER_PORT
|
||||
from cashu.core.settings import settings
|
||||
|
||||
|
||||
@click.command(
|
||||
@@ -13,15 +13,15 @@ from cashu.core.settings import MINT_SERVER_HOST, MINT_SERVER_PORT
|
||||
allow_extra_args=True,
|
||||
)
|
||||
)
|
||||
@click.option("--port", default=MINT_SERVER_PORT, help="Port to listen on")
|
||||
@click.option("--host", default=MINT_SERVER_HOST, help="Host to run mint on")
|
||||
@click.option("--port", default=settings.mint_listen_port, help="Port to listen on")
|
||||
@click.option("--host", default=settings.mint_listen_host, help="Host to run mint on")
|
||||
@click.option("--ssl-keyfile", default=None, help="Path to SSL keyfile")
|
||||
@click.option("--ssl-certfile", default=None, help="Path to SSL certificate")
|
||||
@click.pass_context
|
||||
def main(
|
||||
ctx: Context,
|
||||
port: int = MINT_SERVER_PORT,
|
||||
host: str = MINT_SERVER_HOST,
|
||||
port: int = settings.mint_listen_port,
|
||||
host: str = settings.mint_listen_host,
|
||||
ssl_keyfile: Optional[str] = None,
|
||||
ssl_certfile: Optional[str] = None,
|
||||
):
|
||||
|
||||
@@ -7,26 +7,24 @@ from loguru import logger
|
||||
|
||||
from cashu.core.db import Database
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import (
|
||||
CASHU_DIR,
|
||||
LIGHTNING,
|
||||
MINT_DATABASE,
|
||||
MINT_LIGHTNING_BACKEND,
|
||||
MINT_PRIVATE_KEY,
|
||||
)
|
||||
from cashu.core.settings import settings
|
||||
from cashu.lightning.fake import FakeWallet # type: ignore
|
||||
from cashu.lightning.lnbits import LNbitsWallet # type: ignore
|
||||
from cashu.mint import migrations
|
||||
from cashu.mint.ledger import Ledger
|
||||
|
||||
logger.debug("Enviroment Settings:")
|
||||
for key, value in settings.dict().items():
|
||||
logger.debug(f"{key}: {value}")
|
||||
|
||||
wallets_module = importlib.import_module("cashu.lightning")
|
||||
LIGHTNING_BACKEND = getattr(wallets_module, MINT_LIGHTNING_BACKEND)()
|
||||
lightning_backend = getattr(wallets_module, settings.mint_lightning_backend)()
|
||||
|
||||
ledger = Ledger(
|
||||
db=Database("mint", MINT_DATABASE),
|
||||
seed=MINT_PRIVATE_KEY,
|
||||
db=Database("mint", settings.mint_database),
|
||||
seed=settings.mint_private_key,
|
||||
derivation_path="0/0/0/0",
|
||||
lightning=LIGHTNING_BACKEND,
|
||||
lightning=lightning_backend,
|
||||
)
|
||||
|
||||
|
||||
@@ -36,8 +34,8 @@ async def start_mint_init():
|
||||
await ledger.load_used_proofs()
|
||||
await ledger.init_keysets()
|
||||
|
||||
if LIGHTNING:
|
||||
logger.info(f"Using backend: {MINT_LIGHTNING_BACKEND}")
|
||||
if settings.lightning:
|
||||
logger.info(f"Using backend: {settings.mint_lightning_backend}")
|
||||
error_message, balance = await ledger.lightning.status()
|
||||
if error_message:
|
||||
logger.warning(
|
||||
@@ -46,5 +44,5 @@ async def start_mint_init():
|
||||
)
|
||||
logger.info(f"Lightning balance: {balance} msat")
|
||||
|
||||
logger.info(f"Data dir: {CASHU_DIR}")
|
||||
logger.info(f"Data dir: {settings.cashu_dir}")
|
||||
logger.info("Mint started.")
|
||||
|
||||
@@ -4,8 +4,8 @@ sys.tracebacklimit = None # type: ignore
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from cashu.core.settings import DEBUG
|
||||
from cashu.core.settings import settings
|
||||
|
||||
# configure logger
|
||||
logger.remove()
|
||||
logger.add(sys.stderr, level="DEBUG" if DEBUG else "INFO")
|
||||
logger.add(sys.stderr, level="DEBUG" if settings.debug else "INFO")
|
||||
|
||||
@@ -21,19 +21,7 @@ from loguru import logger
|
||||
from cashu.core.base import Proof, TokenV2
|
||||
from cashu.core.helpers import sum_proofs
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import (
|
||||
CASHU_DIR,
|
||||
DEBUG,
|
||||
ENV_FILE,
|
||||
LIGHTNING,
|
||||
MINT_URL,
|
||||
NOSTR_PRIVATE_KEY,
|
||||
NOSTR_RELAYS,
|
||||
SOCKS_HOST,
|
||||
SOCKS_PORT,
|
||||
TOR,
|
||||
VERSION,
|
||||
)
|
||||
from cashu.core.settings import settings
|
||||
from cashu.nostr.nostr.client.client import NostrClient
|
||||
from cashu.tor.tor import TorProxy
|
||||
from cashu.wallet import migrations
|
||||
@@ -69,7 +57,12 @@ class NaturalOrderGroup(click.Group):
|
||||
|
||||
|
||||
@click.group(cls=NaturalOrderGroup)
|
||||
@click.option("--host", "-h", default=MINT_URL, help=f"Mint URL (default: {MINT_URL}).")
|
||||
@click.option(
|
||||
"--host",
|
||||
"-h",
|
||||
default=settings.mint_url,
|
||||
help=f"Mint URL (default: {settings.mint_url}).",
|
||||
)
|
||||
@click.option(
|
||||
"--wallet",
|
||||
"-w",
|
||||
@@ -79,24 +72,22 @@ class NaturalOrderGroup(click.Group):
|
||||
)
|
||||
@click.pass_context
|
||||
def cli(ctx: Context, host: str, walletname: str):
|
||||
if TOR and not TorProxy().check_platform():
|
||||
if settings.tor and not TorProxy().check_platform():
|
||||
error_str = "Your settings say TOR=true but the built-in Tor bundle is not supported on your system. You have two options: Either install Tor manually and set TOR=FALSE and SOCKS_HOST=localhost and SOCKS_PORT=9050 in your Cashu config (recommended). Or turn off Tor by setting TOR=false (not recommended). Cashu will not work until you edit your config file accordingly."
|
||||
error_str += "\n\n"
|
||||
if ENV_FILE:
|
||||
error_str += f"Edit your Cashu config file here: {ENV_FILE}"
|
||||
env_path = ENV_FILE
|
||||
if settings.env_file:
|
||||
error_str += f"Edit your Cashu config file here: {settings.env_file}"
|
||||
env_path = settings.env_file
|
||||
else:
|
||||
error_str += (
|
||||
f"Ceate a new Cashu config file here: {os.path.join(CASHU_DIR, '.env')}"
|
||||
)
|
||||
env_path = os.path.join(CASHU_DIR, ".env")
|
||||
error_str += f"Ceate a new Cashu config file here: {os.path.join(settings.cashu_dir, '.env')}"
|
||||
env_path = os.path.join(settings.cashu_dir, ".env")
|
||||
error_str += f'\n\nYou can turn off Tor with this command: echo "TOR=FALSE" >> {env_path}'
|
||||
raise Exception(error_str)
|
||||
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["HOST"] = host
|
||||
ctx.obj["WALLET_NAME"] = walletname
|
||||
wallet = Wallet(ctx.obj["HOST"], os.path.join(CASHU_DIR, walletname))
|
||||
wallet = Wallet(ctx.obj["HOST"], os.path.join(settings.cashu_dir, walletname))
|
||||
ctx.obj["WALLET"] = wallet
|
||||
asyncio.run(init_wallet(wallet))
|
||||
pass
|
||||
@@ -149,7 +140,7 @@ async def invoice(ctx: Context, amount: int, hash: str):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
wallet.status()
|
||||
if not LIGHTNING:
|
||||
if not settings.lightning:
|
||||
r = await wallet.mint(amount)
|
||||
elif amount and not hash:
|
||||
invoice = await wallet.request_mint(amount)
|
||||
@@ -601,13 +592,15 @@ async def invoices(ctx):
|
||||
@coro
|
||||
async def wallets(ctx):
|
||||
# list all directories
|
||||
wallets = [d for d in listdir(CASHU_DIR) if isdir(join(CASHU_DIR, d))]
|
||||
wallets = [
|
||||
d for d in listdir(settings.cashu_dir) if isdir(join(settings.cashu_dir, d))
|
||||
]
|
||||
try:
|
||||
wallets.remove("mint")
|
||||
except ValueError:
|
||||
pass
|
||||
for w in wallets:
|
||||
wallet = Wallet(ctx.obj["HOST"], os.path.join(CASHU_DIR, w))
|
||||
wallet = Wallet(ctx.obj["HOST"], os.path.join(settings.cashu_dir, w))
|
||||
try:
|
||||
await init_wallet(wallet)
|
||||
if wallet.proofs and len(wallet.proofs):
|
||||
@@ -625,20 +618,20 @@ async def wallets(ctx):
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def info(ctx: Context):
|
||||
print(f"Version: {VERSION}")
|
||||
print(f"Version: {settings.version}")
|
||||
print(f"Wallet: {ctx.obj['WALLET_NAME']}")
|
||||
if DEBUG:
|
||||
print(f"Debug: {DEBUG}")
|
||||
print(f"Cashu dir: {CASHU_DIR}")
|
||||
if ENV_FILE:
|
||||
print(f"Settings: {ENV_FILE}")
|
||||
if TOR:
|
||||
print(f"Tor enabled: {TOR}")
|
||||
if NOSTR_PRIVATE_KEY:
|
||||
client = NostrClient(private_key=NOSTR_PRIVATE_KEY, connect=False)
|
||||
if settings.debug:
|
||||
print(f"Debug: {settings.debug}")
|
||||
print(f"Cashu dir: {settings.cashu_dir}")
|
||||
if settings.env_file:
|
||||
print(f"Settings: {settings.env_file}")
|
||||
if settings.tor:
|
||||
print(f"Tor enabled: {settings.tor}")
|
||||
if settings.nostr_private_key:
|
||||
client = NostrClient(private_key=settings.nostr_private_key, connect=False)
|
||||
print(f"Nostr public key: {client.public_key.bech32()}")
|
||||
print(f"Nostr relays: {NOSTR_RELAYS}")
|
||||
if SOCKS_HOST:
|
||||
print(f"Socks proxy: {SOCKS_HOST}:{SOCKS_PORT}")
|
||||
print(f"Nostr relays: {settings.nostr_relays}")
|
||||
if settings.socks_host:
|
||||
print(f"Socks proxy: {settings.socks_host}:{settings.socks_port}")
|
||||
print(f"Mint URL: {ctx.obj['HOST']}")
|
||||
return
|
||||
|
||||
@@ -8,7 +8,7 @@ from loguru import logger
|
||||
|
||||
from cashu.core.base import Proof, TokenV2, TokenV2Mint, WalletKeyset
|
||||
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 settings
|
||||
from cashu.wallet.crud import get_keyset
|
||||
from cashu.wallet.wallet import Wallet as Wallet
|
||||
|
||||
@@ -32,7 +32,7 @@ async def verify_mints(ctx: Context, token: TokenV2):
|
||||
for keyset in set([id for id in mint.ids if id in proofs_keysets]):
|
||||
# init a temporary wallet object
|
||||
keyset_wallet = Wallet(
|
||||
mint.url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"])
|
||||
mint.url, os.path.join(settings.cashu_dir, ctx.obj["WALLET_NAME"])
|
||||
)
|
||||
# make sure that this mint supports this keyset
|
||||
mint_keysets = await keyset_wallet._get_keyset_ids(mint.url)
|
||||
@@ -81,7 +81,7 @@ async def redeem_multimint(ctx: Context, token: TokenV2, script, signature):
|
||||
logger.debug(f"Redeeming tokens from keyset {keyset}")
|
||||
# init a temporary wallet object
|
||||
keyset_wallet = Wallet(
|
||||
mint.url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"])
|
||||
mint.url, os.path.join(settings.cashu_dir, ctx.obj["WALLET_NAME"])
|
||||
)
|
||||
|
||||
# load the keys
|
||||
@@ -147,7 +147,9 @@ async def get_mint_wallet(ctx: Context):
|
||||
mint_url = list(mint_balances.keys())[mint_nr - 1]
|
||||
|
||||
# load this mint_url into a wallet
|
||||
mint_wallet = Wallet(mint_url, os.path.join(CASHU_DIR, ctx.obj["WALLET_NAME"]))
|
||||
mint_wallet = Wallet(
|
||||
mint_url, os.path.join(settings.cashu_dir, ctx.obj["WALLET_NAME"])
|
||||
)
|
||||
mint_keysets: WalletKeyset = await get_keyset(mint_url=mint_url, db=mint_wallet.db) # type: ignore
|
||||
|
||||
# load the keys
|
||||
@@ -196,7 +198,8 @@ async def proofs_to_serialized_tokenv2(wallet, proofs: List[Proof], url: str):
|
||||
url = ks.mint_url if ks and ks.mint_url else ""
|
||||
|
||||
url = url or (
|
||||
input(f"Enter mint URL (press enter for default {MINT_URL}): ") or MINT_URL
|
||||
input(f"Enter mint URL (press enter for default {settings.mint_url}): ")
|
||||
or settings.mint_url
|
||||
)
|
||||
|
||||
token.mints.append(TokenV2Mint(url=url, ids=keysets))
|
||||
|
||||
@@ -6,7 +6,7 @@ import click
|
||||
from click import Context
|
||||
from requests.exceptions import ConnectionError
|
||||
|
||||
from cashu.core.settings import NOSTR_PRIVATE_KEY, NOSTR_RELAYS
|
||||
from cashu.core.settings import settings
|
||||
from cashu.nostr.nostr.client.client import NostrClient
|
||||
from cashu.nostr.nostr.event import Event
|
||||
from cashu.nostr.nostr.key import PublicKey
|
||||
@@ -79,8 +79,10 @@ async def send_nostr(ctx: Context, amount: int, pubkey: str, verbose: bool, yes:
|
||||
default=True,
|
||||
)
|
||||
|
||||
client = NostrClient(private_key=NOSTR_PRIVATE_KEY or "", relays=NOSTR_RELAYS)
|
||||
if verbose and not NOSTR_PRIVATE_KEY:
|
||||
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()}")
|
||||
|
||||
@@ -91,12 +93,14 @@ async def send_nostr(ctx: Context, amount: int, pubkey: str, verbose: bool, yes:
|
||||
|
||||
|
||||
async def receive_nostr(ctx: Context, verbose: bool):
|
||||
if NOSTR_PRIVATE_KEY is None:
|
||||
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=NOSTR_PRIVATE_KEY, relays=NOSTR_RELAYS)
|
||||
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()}")
|
||||
|
||||
@@ -42,7 +42,7 @@ from cashu.core.script import (
|
||||
step2_carol_sign_tx,
|
||||
)
|
||||
from cashu.core.secp import PublicKey
|
||||
from cashu.core.settings import DEBUG, MAX_ORDER, SOCKS_HOST, SOCKS_PORT, TOR, VERSION
|
||||
from cashu.core.settings import settings
|
||||
from cashu.core.split import amount_split
|
||||
from cashu.tor.tor import TorProxy
|
||||
from cashu.wallet.crud import (
|
||||
@@ -67,16 +67,16 @@ def async_set_requests(func):
|
||||
"""
|
||||
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
self.s.headers.update({"Client-version": VERSION})
|
||||
if DEBUG:
|
||||
self.s.headers.update({"Client-version": settings.version})
|
||||
if settings.debug:
|
||||
self.s.verify = False
|
||||
socks_host, socks_port = None, None
|
||||
if TOR and TorProxy().check_platform():
|
||||
if settings.tor and TorProxy().check_platform():
|
||||
self.tor = TorProxy(timeout=True)
|
||||
self.tor.run_daemon(verbose=True)
|
||||
socks_host, socks_port = "localhost", 9050
|
||||
else:
|
||||
socks_host, socks_port = SOCKS_HOST, SOCKS_PORT
|
||||
socks_host, socks_port = settings.socks_host, settings.socks_port
|
||||
|
||||
if socks_host and socks_port:
|
||||
proxies = {
|
||||
@@ -515,8 +515,10 @@ class Wallet(LedgerAPI):
|
||||
List[Proof]: Newly minted proofs.
|
||||
"""
|
||||
for amount in amounts:
|
||||
if amount not in [2**i for i in range(MAX_ORDER)]:
|
||||
raise Exception(f"Can only mint amounts with 2^n up to {2**MAX_ORDER}.")
|
||||
if amount not in [2**i for i in range(settings.max_order)]:
|
||||
raise Exception(
|
||||
f"Can only mint amounts with 2^n up to {2**settings.max_order}."
|
||||
)
|
||||
proofs = await super().mint(amounts, payment_hash)
|
||||
if proofs == []:
|
||||
raise Exception("received no proofs.")
|
||||
|
||||
@@ -4,7 +4,7 @@ import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import VERSION
|
||||
from cashu.core.settings import settings
|
||||
from cashu.wallet import migrations
|
||||
from cashu.wallet.cli.cli import cli
|
||||
from cashu.wallet.wallet import Wallet
|
||||
@@ -29,7 +29,7 @@ def test_info():
|
||||
)
|
||||
print("INFO")
|
||||
print(result.output)
|
||||
result.output.startswith(f"Version: {VERSION}")
|
||||
result.output.startswith(f"Version: {settings.version}")
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ SERVER_ENDPOINT = "http://localhost:3338"
|
||||
import os
|
||||
|
||||
from cashu.core.db import Database
|
||||
from cashu.core.settings import MAX_ORDER
|
||||
from cashu.core.settings import settings
|
||||
from cashu.mint import migrations
|
||||
from cashu.mint.ledger import Ledger
|
||||
|
||||
@@ -63,7 +63,7 @@ async def test_keysets(ledger: Ledger):
|
||||
async def test_get_keyset(ledger: Ledger):
|
||||
keyset = ledger.get_keyset()
|
||||
assert type(keyset) == dict
|
||||
assert len(keyset) == MAX_ORDER
|
||||
assert len(keyset) == settings.max_order
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest_asyncio
|
||||
from cashu.core.base import Proof
|
||||
from cashu.core.helpers import async_unwrap, sum_proofs
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import MAX_ORDER
|
||||
from cashu.core.settings import settings
|
||||
from cashu.wallet import migrations
|
||||
from cashu.wallet.wallet import Wallet
|
||||
from cashu.wallet.wallet import Wallet as Wallet1
|
||||
@@ -51,7 +51,7 @@ async def wallet2(mint):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_keys(wallet1: Wallet):
|
||||
assert wallet1.keys.public_keys
|
||||
assert len(wallet1.keys.public_keys) == MAX_ORDER
|
||||
assert len(wallet1.keys.public_keys) == settings.max_order
|
||||
keyset = await wallet1._get_keys(wallet1.url)
|
||||
assert keyset.id is not None
|
||||
assert type(keyset.id) == str
|
||||
@@ -61,7 +61,7 @@ async def test_get_keys(wallet1: Wallet):
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_keyset(wallet1: Wallet):
|
||||
assert wallet1.keys.public_keys
|
||||
assert len(wallet1.keys.public_keys) == MAX_ORDER
|
||||
assert len(wallet1.keys.public_keys) == settings.max_order
|
||||
# let's get the keys first so we can get a keyset ID that we use later
|
||||
keys1 = await wallet1._get_keys(wallet1.url)
|
||||
# gets the keys of a specific keyset
|
||||
@@ -107,7 +107,7 @@ async def test_mint_amounts_wrong_order(wallet1: Wallet):
|
||||
"""Mint amount that is not part in 2^n"""
|
||||
await assert_err(
|
||||
wallet1.mint_amounts([1, 2, 3]),
|
||||
f"Can only mint amounts with 2^n up to {2**MAX_ORDER}.",
|
||||
f"Can only mint amounts with 2^n up to {2**settings.max_order}.",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user