Merge pull request #15 from callebtc/mint_router

Mint router
This commit is contained in:
calle
2022-09-29 01:28:48 +02:00
committed by GitHub
38 changed files with 328 additions and 308 deletions

View File

@@ -6,6 +6,17 @@
Cashu is an Ecash implementation based on David Wagner's variant of Chaumian blinding. Token logic based on [minicash](https://github.com/phyro/minicash) ([description](https://gist.github.com/phyro/935badc682057f418842c72961cf096c)) which implements a [Blind Diffie-Hellman Key Exchange](https://cypherpunks.venona.com/date/1996/03/msg01848.html) scheme written down by Ruben Somsen [here](https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406). The database mechanics and the Lightning backend uses parts from [LNbits](https://github.com/lnbits/lnbits-legend). Cashu is an Ecash implementation based on David Wagner's variant of Chaumian blinding. Token logic based on [minicash](https://github.com/phyro/minicash) ([description](https://gist.github.com/phyro/935badc682057f418842c72961cf096c)) which implements a [Blind Diffie-Hellman Key Exchange](https://cypherpunks.venona.com/date/1996/03/msg01848.html) scheme written down by Ruben Somsen [here](https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406). The database mechanics and the Lightning backend uses parts from [LNbits](https://github.com/lnbits/lnbits-legend).
<p align="center">
Quick links:
<a href="#cashu-client-protocol">Cashu client protocol</a> ·
<a href="#easy-install">Quick Install</a> ·
<a href="#hard-install-poetry">Manual install</a> ·
<a href="#configuration">Configuration</a> ·
<a href="#using-cashu">Using Cashu</a> ·
<a href="#running-a-mint">Run a mint</a>
<br><br>
</p>
## Cashu client protocol ## Cashu client protocol
There are ongoing efforts to implement alternative Cashu clients that use the same protocol such as a [Cashu Javascript wallet](https://github.com/motorina0/cashu-js-wallet). If you are interested in helping with Cashu development, please see the [docs](docs/) for the notation and conventions used. There are ongoing efforts to implement alternative Cashu clients that use the same protocol such as a [Cashu Javascript wallet](https://github.com/motorina0/cashu-js-wallet). If you are interested in helping with Cashu development, please see the [docs](docs/) for the notation and conventions used.
@@ -20,8 +31,8 @@ To update Cashu, use `pip install cashu -U`. If you have problems running the co
You can skip the entire next section about Poetry and jump right to [Using Cashu](#using-cashu). You can skip the entire next section about Poetry and jump right to [Using Cashu](#using-cashu).
### Hard install: Poetry ## Hard install: Poetry
These steps help you install Python via pyenv and Poetry. If you already have Poetry running on your computer, you can skip this step and jump right to [Install Cashu](#install-cashu). These steps help you install Python via pyenv and Poetry. If you already have Poetry running on your computer, you can skip this step and jump right to [Install Cashu](#poetry-install-cashu).
#### Poetry: Prerequisites #### Poetry: Prerequisites
@@ -168,7 +179,7 @@ Balance: 351 sat (Available: 351 sat in 7 tokens)
Balance: 339 sat (Available: 339 sat in 8 tokens) Balance: 339 sat (Available: 339 sat in 8 tokens)
``` ```
## Run a mint yourself # Running a mint
This command runs the mint on your local computer. Skip this step if you want to use the [public test mint](#test-instance) instead. This command runs the mint on your local computer. Skip this step if you want to use the [public test mint](#test-instance) instead.
```bash ```bash
mint mint

View File

@@ -1,7 +1,7 @@
import asyncio import asyncio
from functools import partial, wraps from functools import partial, wraps
from core.settings import LIGHTNING_FEE_PERCENT, LIGHTNING_RESERVE_FEE_MIN from cashu.core.settings import LIGHTNING_FEE_PERCENT, LIGHTNING_RESERVE_FEE_MIN
def async_wrap(func): def async_wrap(func):

View File

@@ -2,7 +2,7 @@ import re
from loguru import logger from loguru import logger
from core.db import COCKROACH, POSTGRES, SQLITE, Database from cashu.core.db import COCKROACH, POSTGRES, SQLITE, Database
async def migrate_databases(db: Database, migrations_module): async def migrate_databases(db: Database, migrations_module):

View File

@@ -32,3 +32,4 @@ LNBITS_ENDPOINT = env.str("LNBITS_ENDPOINT", default=None)
LNBITS_KEY = env.str("LNBITS_KEY", default=None) LNBITS_KEY = env.str("LNBITS_KEY", default=None)
MAX_ORDER = 64 MAX_ORDER = 64
VERSION = "0.1.10"

View File

@@ -0,0 +1,3 @@
from cashu.lightning.lnbits import LNbitsWallet
WALLET = LNbitsWallet()

View File

@@ -6,10 +6,15 @@ from typing import AsyncGenerator, Dict, Optional
import requests import requests
from core.settings import LNBITS_ENDPOINT, LNBITS_KEY from cashu.core.settings import LNBITS_ENDPOINT, LNBITS_KEY
from .base import (InvoiceResponse, PaymentResponse, PaymentStatus, from .base import (
StatusResponse, Wallet) InvoiceResponse,
PaymentResponse,
PaymentStatus,
StatusResponse,
Wallet,
)
class LNbitsWallet(Wallet): class LNbitsWallet(Wallet):

4
cashu/mint/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
from cashu.core.settings import MINT_PRIVATE_KEY
from cashu.mint.ledger import Ledger
ledger = Ledger(MINT_PRIVATE_KEY, "data/mint")

3
cashu/mint/__main__.py Normal file
View File

@@ -0,0 +1,3 @@
from .main import main
main()

71
cashu/mint/app.py Normal file
View File

@@ -0,0 +1,71 @@
import asyncio
import logging
import sys
from fastapi import FastAPI
from loguru import logger
from cashu.core.settings import CASHU_DIR, DEBUG
from cashu.lightning import WALLET
from cashu.mint.migrations import m001_initial
from . import ledger
from .router import router
from .startup import load_ledger
def create_app(config_object="core.settings") -> FastAPI:
def configure_logger() -> None:
class Formatter:
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:
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
def format(self, record):
function = "{function}".format(**record)
if function == "emit": # uvicorn logs
return self.minimal_fmt
return self.fmt
class InterceptHandler(logging.Handler):
def emit(self, record):
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
logger.log(level, record.getMessage())
logger.remove()
log_level: str = "INFO"
formatter = Formatter()
logger.add(sys.stderr, level=log_level, format=formatter.format)
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
configure_logger()
app = FastAPI(
title="Cashu Mint",
description="Ecash wallet and mint.",
license_info={
"name": "MIT License",
"url": "https://raw.githubusercontent.com/callebtc/cashu/main/LICENSE",
},
)
return app
app = create_app()
app.include_router(router=router)
@app.on_event("startup")
async def startup_load_ledger():
await load_ledger()

View File

@@ -1,8 +1,8 @@
import secrets import secrets
from typing import Optional from typing import Optional
from core.base import Invoice, Proof from cashu.core.base import Invoice, Proof
from core.db import Connection, Database from cashu.core.db import Connection, Database
async def store_promise( async def store_promise(

View File

@@ -5,15 +5,15 @@ Implementation of https://gist.github.com/phyro/935badc682057f418842c72961cf096c
import hashlib import hashlib
from typing import List, Set from typing import List, Set
import core.b_dhke as b_dhke import cashu.core.b_dhke as b_dhke
from core.base import BlindedMessage, BlindedSignature, Invoice, Proof from cashu.core.base import BlindedMessage, BlindedSignature, Invoice, Proof
from core.db import Database from cashu.core.db import Database
from core.helpers import fee_reserve from cashu.core.helpers import fee_reserve
from core.secp import PrivateKey, PublicKey from cashu.core.secp import PrivateKey, PublicKey
from core.settings import LIGHTNING, MAX_ORDER from cashu.core.settings import LIGHTNING, MAX_ORDER
from core.split import amount_split from cashu.core.split import amount_split
from lightning import WALLET from cashu.lightning import WALLET
from mint.crud import ( from cashu.mint.crud import (
get_lightning_invoice, get_lightning_invoice,
get_proofs_used, get_proofs_used,
invalidate_proof, invalidate_proof,

49
cashu/mint/main.py Normal file
View File

@@ -0,0 +1,49 @@
import click
import uvicorn
from cashu.core.settings import MINT_SERVER_HOST, MINT_SERVER_PORT
@click.command(
context_settings=dict(
ignore_unknown_options=True,
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("--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,
port: int = MINT_SERVER_PORT,
host: str = MINT_SERVER_HOST,
ssl_keyfile: str = None,
ssl_certfile: str = None,
):
"""Launched with `poetry run mint` at root level"""
# this beautiful beast parses all command line arguments and passes them to the uvicorn server
d = dict()
for a in ctx.args:
item = a.split("=")
if len(item) > 1: # argument like --key=value
print(a, item)
d[item[0].strip("--").replace("-", "_")] = (
int(item[1]) # need to convert to int if it's a number
if item[1].isdigit()
else item[1]
)
else:
d[a.strip("--")] = True # argument like --key
config = uvicorn.Config(
"cashu.mint.app:app",
port=port,
host=host,
ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile,
**d,
)
server = uvicorn.Server(config)
server.run()

View File

@@ -1,4 +1,4 @@
from core.db import Database from cashu.core.db import Database
async def m000_create_migrations_table(db): async def m000_create_migrations_table(db):

88
cashu/mint/router.py Normal file
View File

@@ -0,0 +1,88 @@
from typing import Union
from fastapi import APIRouter
from secp256k1 import PublicKey
from cashu.core.base import CheckPayload, MeltPayload, MintPayloads, SplitPayload
from cashu.mint import ledger
router: APIRouter = APIRouter()
@router.get("/keys")
def keys():
"""Get the public keys of the mint"""
return ledger.get_pubkeys()
@router.get("/mint")
async def request_mint(amount: int = 0):
"""Request minting of tokens. Server responds with a Lightning invoice."""
payment_request, payment_hash = await ledger.request_mint(amount)
print(f"Lightning invoice: {payment_request}")
return {"pr": payment_request, "hash": payment_hash}
@router.post("/mint")
async def mint(payloads: MintPayloads, payment_hash: Union[str, None] = None):
"""
Requests the minting of tokens belonging to a paid payment request.
Parameters:
pr: payment_request of the Lightning paid invoice.
Body (JSON):
payloads: contains a list of blinded messages waiting to be signed.
NOTE:
- This needs to be replaced by the preimage otherwise someone knowing
the payment_request can request the tokens instead of the rightful
owner.
- The blinded message should ideally be provided to the server *before* payment
in the GET /mint endpoint so that the server knows to sign only these tokens
when the invoice is paid.
"""
amounts = []
B_s = []
for payload in payloads.blinded_messages:
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
try:
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
return promises
except Exception as exc:
return {"error": str(exc)}
@router.post("/melt")
async def melt(payload: MeltPayload):
"""
Requests tokens to be destroyed and sent out via Lightning.
"""
ok, preimage = await ledger.melt(payload.proofs, payload.amount, payload.invoice)
return {"paid": ok, "preimage": preimage}
@router.post("/check")
async def check_spendable(payload: CheckPayload):
return await ledger.check_spendable(payload.proofs)
@router.post("/split")
async def split(payload: SplitPayload):
"""
Requetst a set of tokens with amount "total" to be split into two
newly minted sets with amount "split" and "total-split".
"""
proofs = payload.proofs
amount = payload.amount
output_data = payload.output_data.blinded_messages
try:
split_return = await ledger.split(proofs, amount, output_data)
except Exception as exc:
return {"error": str(exc)}
if not split_return:
"""There was a problem with the split"""
raise Exception("could not split tokens.")
fst_promises, snd_promises = split_return
return {"fst": fst_promises, "snd": snd_promises}

25
cashu/mint/startup.py Normal file
View File

@@ -0,0 +1,25 @@
import asyncio
from loguru import logger
from cashu.core.settings import CASHU_DIR
from cashu.lightning import WALLET
from cashu.mint.migrations import m001_initial
from . import ledger
async def load_ledger():
await asyncio.wait([m001_initial(ledger.db)])
await ledger.load_used_proofs()
error_message, balance = await WALLET.status()
if error_message:
logger.warning(
f"The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
RuntimeWarning,
)
logger.info(f"Lightning balance: {balance} sat")
logger.info(f"Data dir: {CASHU_DIR}")
logger.info("Mint started.")

3
cashu/wallet/__main__.py Normal file
View File

@@ -0,0 +1,3 @@
from .cli import cli
cli()

View File

@@ -12,15 +12,15 @@ from operator import itemgetter
import click import click
from bech32 import bech32_decode, bech32_encode, convertbits from bech32 import bech32_decode, bech32_encode, convertbits
import core.bolt11 as bolt11 import cashu.core.bolt11 as bolt11
from core.base import Proof from cashu.core.base import Proof
from core.bolt11 import Invoice from cashu.core.bolt11 import Invoice
from core.helpers import fee_reserve from cashu.core.helpers import fee_reserve
from core.migrations import migrate_databases from cashu.core.migrations import migrate_databases
from core.settings import CASHU_DIR, DEBUG, LIGHTNING, MINT_URL from cashu.core.settings import CASHU_DIR, DEBUG, LIGHTNING, MINT_URL, VERSION
from wallet import migrations from cashu.wallet import migrations
from wallet.crud import get_reserved_proofs from cashu.wallet.crud import get_reserved_proofs
from wallet.wallet import Wallet as Wallet from cashu.wallet.wallet import Wallet as Wallet
async def init_wallet(wallet: Wallet): async def init_wallet(wallet: Wallet):
@@ -203,9 +203,7 @@ async def pay(ctx, invoice: str):
@click.pass_context @click.pass_context
@coro @coro
async def info(ctx): async def info(ctx):
wallet: Wallet = ctx.obj["WALLET"] print(f"Version: {VERSION}")
await init_wallet(wallet)
wallet.status()
print(f"Debug: {DEBUG}") print(f"Debug: {DEBUG}")
print(f"Cashu dir: {CASHU_DIR}") print(f"Cashu dir: {CASHU_DIR}")
print(f"Mint URL: {MINT_URL}") print(f"Mint URL: {MINT_URL}")

View File

@@ -1,8 +1,8 @@
import time import time
from typing import Optional from typing import Optional
from core.base import Proof from cashu.core.base import Proof
from core.db import Connection, Database from cashu.core.db import Connection, Database
async def store_proof( async def store_proof(

View File

@@ -1,4 +1,4 @@
from core.db import Database from cashu.core.db import Database
async def m000_create_migrations_table(db): async def m000_create_migrations_table(db):

View File

@@ -1,14 +1,13 @@
import base64 import base64
import json import json
import random
import secrets as scrts import secrets as scrts
import uuid import uuid
from typing import List from typing import List
import requests import requests
import core.b_dhke as b_dhke import cashu.core.b_dhke as b_dhke
from core.base import ( from cashu.core.base import (
BlindedMessage, BlindedMessage,
BlindedSignature, BlindedSignature,
CheckPayload, CheckPayload,
@@ -17,11 +16,16 @@ from core.base import (
Proof, Proof,
SplitPayload, SplitPayload,
) )
from core.db import Database from cashu.core.db import Database
from core.secp import PublicKey from cashu.core.secp import PublicKey
from core.settings import DEBUG from cashu.core.settings import DEBUG
from core.split import amount_split from cashu.core.split import amount_split
from wallet.crud import get_proofs, invalidate_proof, store_proof, update_proof_reserved from cashu.wallet.crud import (
get_proofs,
invalidate_proof,
store_proof,
update_proof_reserved,
)
class LedgerAPI: class LedgerAPI:
@@ -97,6 +101,7 @@ class LedgerAPI:
fst_outputs = amount_split(fst_amt) fst_outputs = amount_split(fst_amt)
snd_outputs = amount_split(snd_amt) snd_outputs = amount_split(snd_amt)
# TODO: Refactor together with the same procedure in self.mint()
secrets = [] secrets = []
payloads: MintPayloads = MintPayloads() payloads: MintPayloads = MintPayloads()
for output_amt in fst_outputs + snd_outputs: for output_amt in fst_outputs + snd_outputs:

BIN
cashu/wallet/wallet_live/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
from lightning.lnbits import LNbitsWallet
WALLET = LNbitsWallet()

View File

@@ -1,217 +0,0 @@
import asyncio
import logging
import sys
from typing import Union
import click
import uvicorn
from fastapi import FastAPI
from loguru import logger
from secp256k1 import PublicKey
import core.settings as settings
from core.base import CheckPayload, MeltPayload, MintPayloads, SplitPayload
from core.settings import (CASHU_DIR, MINT_PRIVATE_KEY, MINT_SERVER_HOST,
MINT_SERVER_PORT)
from lightning import WALLET
from mint.ledger import Ledger
from mint.migrations import m001_initial
ledger = Ledger(MINT_PRIVATE_KEY, "data/mint")
def startup(app: FastAPI):
@app.on_event("startup")
async def load_ledger():
await asyncio.wait([m001_initial(ledger.db)])
await ledger.load_used_proofs()
error_message, balance = await WALLET.status()
if error_message:
logger.warning(
f"The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
RuntimeWarning,
)
logger.info(f"Lightning balance: {balance} sat")
logger.info(f"Data dir: {CASHU_DIR}")
logger.info("Mint started.")
def create_app(config_object="core.settings") -> FastAPI:
def configure_logger() -> None:
class Formatter:
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 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
def format(self, record):
function = "{function}".format(**record)
if function == "emit": # uvicorn logs
return self.minimal_fmt
return self.fmt
class InterceptHandler(logging.Handler):
def emit(self, record):
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
logger.log(level, record.getMessage())
logger.remove()
log_level: str = "INFO"
formatter = Formatter()
logger.add(sys.stderr, level=log_level, format=formatter.format)
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
configure_logger()
app = FastAPI(
title="Cashu Mint",
description="Ecash wallet and mint.",
license_info={
"name": "MIT License",
"url": "https://raw.githubusercontent.com/callebtc/cashu/main/LICENSE",
},
)
startup(app)
return app
app = create_app()
@app.get("/keys")
def keys():
"""Get the public keys of the mint"""
return ledger.get_pubkeys()
@app.get("/mint")
async def request_mint(amount: int = 0):
"""Request minting of tokens. Server responds with a Lightning invoice."""
payment_request, payment_hash = await ledger.request_mint(amount)
print(f"Lightning invoice: {payment_request}")
return {"pr": payment_request, "hash": payment_hash}
@app.post("/mint")
async def mint(payloads: MintPayloads, payment_hash: Union[str, None] = None):
"""
Requests the minting of tokens belonging to a paid payment request.
Parameters:
pr: payment_request of the Lightning paid invoice.
Body (JSON):
payloads: contains a list of blinded messages waiting to be signed.
NOTE:
- This needs to be replaced by the preimage otherwise someone knowing
the payment_request can request the tokens instead of the rightful
owner.
- The blinded message should ideally be provided to the server *before* payment
in the GET /mint endpoint so that the server knows to sign only these tokens
when the invoice is paid.
"""
amounts = []
B_s = []
for payload in payloads.blinded_messages:
amounts.append(payload.amount)
B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
try:
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
return promises
except Exception as exc:
return {"error": str(exc)}
@app.post("/melt")
async def melt(payload: MeltPayload):
"""
Requests tokens to be destroyed and sent out via Lightning.
"""
ok, preimage = await ledger.melt(payload.proofs, payload.amount, payload.invoice)
return {"paid": ok, "preimage": preimage}
@app.post("/check")
async def check_spendable(payload: CheckPayload):
return await ledger.check_spendable(payload.proofs)
@app.post("/split")
async def split(payload: SplitPayload):
"""
Requetst a set of tokens with amount "total" to be split into two
newly minted sets with amount "split" and "total-split".
"""
proofs = payload.proofs
amount = payload.amount
output_data = payload.output_data.blinded_messages
try:
split_return = await ledger.split(proofs, amount, output_data)
except Exception as exc:
return {"error": str(exc)}
if not split_return:
"""There was a problem with the split"""
raise Exception("could not split tokens.")
fst_promises, snd_promises = split_return
return {"fst": fst_promises, "snd": snd_promises}
@click.command(
context_settings=dict(
ignore_unknown_options=True,
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("--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,
port: int = MINT_SERVER_PORT,
host: str = MINT_SERVER_HOST,
ssl_keyfile: str = None,
ssl_certfile: str = None,
):
"""Launched with `poetry run mint` at root level"""
# this beautiful beast parses all command line arguments and passes them to the uvicorn server
d = dict()
for a in ctx.args:
item = a.split("=")
if len(item) > 1: # argument like --key=value
print(a, item)
d[item[0].strip("--").replace("-", "_")] = (
int(item[1]) # need to convert to int if it's a number
if item[1].isdigit()
else item[1]
)
else:
d[a.strip("--")] = True # argument like --key
config = uvicorn.Config(
"mint.app:app",
port=port,
host=host,
ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile,
**d,
)
server = uvicorn.Server(config)
server.run()
if __name__ == "__main__":
main()

39
poetry.lock generated
View File

@@ -85,7 +85,7 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2022.9.14" version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
category = "main" category = "main"
optional = false optional = false
@@ -184,32 +184,13 @@ dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt]
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"] doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
[[package]]
name = "Flask"
version = "2.2.2"
description = "A simple framework for building complex web applications."
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
click = ">=8.0"
importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
itsdangerous = ">=2.0"
Jinja2 = ">=3.0"
Werkzeug = ">=2.2.2"
[package.extras]
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.13.0" version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
typing-extensions = {version = "*", markers = "python_version < \"3.8\""} typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
@@ -709,7 +690,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "d5ee88384ec1ec1774f9fab845d7e8ac6e8ab1859506bcce178f6783b98e535c" content-hash = "4058e11929b71dee85bceb89e1fb5254411db9bde0e087f65b5cf7c0c7fbae8f"
[metadata.files] [metadata.files]
anyio = [ anyio = [
@@ -759,8 +740,8 @@ black = [
{file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
] ]
certifi = [ certifi = [
{file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"}, {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
{file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"}, {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
] ]
cffi = [ cffi = [
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
@@ -852,13 +833,9 @@ fastapi = [
{file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"}, {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"},
{file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"}, {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
] ]
Flask = [
{file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"},
{file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"},
]
h11 = [ h11 = [
{file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"}, {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
] ]
idna = [ idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "cashu" name = "cashu"
version = "0.1.9" version = "0.1.10"
description = "Ecash wallet and mint." description = "Ecash wallet and mint."
authors = ["calle <callebtc@protonmail.com>"] authors = ["calle <callebtc@protonmail.com>"]
license = "MIT" license = "MIT"
@@ -13,7 +13,6 @@ SQLAlchemy = "1.3.24"
sqlalchemy-aio = "0.17.0" sqlalchemy-aio = "0.17.0"
charset-normalizer = "2.0.12" charset-normalizer = "2.0.12"
click = "8.0.4" click = "8.0.4"
Flask = "2.2.2"
idna = "3.3" idna = "3.3"
itsdangerous = "2.1.1" itsdangerous = "2.1.1"
Jinja2 = "3.0.3" Jinja2 = "3.0.3"
@@ -46,6 +45,6 @@ requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts] [tool.poetry.scripts]
mint = "mint.app:main" mint = "cashu.mint.main:main"
cashu = "wallet.cashu:cli" cashu = "cashu.wallet.cli:cli"
wallet-test = "tests.test_wallet:test" wallet-test = "tests.test_wallet:test"

View File

@@ -9,11 +9,11 @@ with open(path.join(this_directory, "README.md"), encoding="utf-8") as f:
with open("requirements.txt") as f: with open("requirements.txt") as f:
requirements = f.read().splitlines() requirements = f.read().splitlines()
entry_points = {"console_scripts": ["cashu = wallet.cashu:cli"]} entry_points = {"console_scripts": ["cashu = cahu.wallet.cli:cli"]}
setuptools.setup( setuptools.setup(
name="cashu", name="cashu",
version="0.1.9", version="0.1.10",
description="Ecash wallet and mint with Bitcoin Lightning support", description="Ecash wallet and mint with Bitcoin Lightning support",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@@ -1,10 +1,8 @@
import asyncio from cashu.core.helpers import async_unwrap
from cashu.core.migrations import migrate_databases
from core.helpers import async_unwrap from cashu.wallet import migrations
from core.migrations import migrate_databases from cashu.wallet.wallet import Wallet as Wallet1
from wallet import migrations from cashu.wallet.wallet import Wallet as Wallet2
from wallet.wallet import Wallet as Wallet1
from wallet.wallet import Wallet as Wallet2
SERVER_ENDPOINT = "http://localhost:3338" SERVER_ENDPOINT = "http://localhost:3338"
@@ -29,7 +27,7 @@ async def run_test():
await migrate_databases(wallet1.db, migrations) await migrate_databases(wallet1.db, migrations)
wallet1.status() wallet1.status()
wallet2 = Wallet1(SERVER_ENDPOINT, "data/wallet2", "wallet2") wallet2 = Wallet2(SERVER_ENDPOINT, "data/wallet2", "wallet2")
await migrate_databases(wallet2.db, migrations) await migrate_databases(wallet2.db, migrations)
wallet2.status() wallet2.status()