mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 18:44:20 +01:00
isort
This commit is contained in:
31
cashu
31
cashu
@@ -1,17 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from wallet.wallet import Wallet as Wallet
|
||||
from wallet.migrations import m001_initial
|
||||
import asyncio
|
||||
import click
|
||||
import json
|
||||
import base64
|
||||
from bech32 import bech32_encode, bech32_decode, convertbits
|
||||
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from functools import wraps
|
||||
|
||||
import click
|
||||
from bech32 import bech32_decode, bech32_encode, convertbits
|
||||
|
||||
from wallet.migrations import m001_initial
|
||||
from wallet.wallet import Wallet as Wallet
|
||||
|
||||
|
||||
# https://github.com/pallets/click/issues/85#issuecomment-503464628
|
||||
def coro(f):
|
||||
@@ -23,20 +22,26 @@ def coro(f):
|
||||
|
||||
|
||||
@click.command("mint")
|
||||
@click.option("--host", default="http://localhost:3338", help="Mint tokens.")
|
||||
@click.option("--wallet", default="wallet", help="Mint tokens.")
|
||||
@click.option("--host", default="http://localhost:3338", help="Mint hostname.")
|
||||
@click.option("--wallet", default="wallet", help="Wallet to use.")
|
||||
@click.option("--mint", default=0, help="Mint tokens.")
|
||||
@click.option("--hash", default="", help="Hash of the paid invoice.")
|
||||
@click.option("--send", default=0, help="Send tokens.")
|
||||
@click.option("--receive", default="", help="Receive tokens.")
|
||||
@click.option("--invalidate", default="", help="Invalidate tokens.")
|
||||
@coro
|
||||
async def main(host, wallet, mint, send, receive, invalidate):
|
||||
async def main(host, wallet, mint, hash, send, receive, invalidate):
|
||||
wallet = Wallet(host, f"data/{wallet}", wallet)
|
||||
await m001_initial(db=wallet.db)
|
||||
await wallet.load_proofs()
|
||||
if mint:
|
||||
if mint and not hash:
|
||||
print(f"Balance: {wallet.balance}")
|
||||
await wallet.mint(mint)
|
||||
r = await wallet.request_mint(mint)
|
||||
print(r)
|
||||
|
||||
if mint and hash:
|
||||
print(f"Balance: {wallet.balance}")
|
||||
await wallet.mint(mint, hash)
|
||||
print(f"Balance: {wallet.balance}")
|
||||
|
||||
if send:
|
||||
|
||||
@@ -30,9 +30,9 @@ If true, C must have originated from Alice
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from ecc.curve import secp256k1, Point
|
||||
from ecc.key import gen_keypair
|
||||
|
||||
from ecc.curve import Point, secp256k1
|
||||
from ecc.key import gen_keypair
|
||||
|
||||
G = secp256k1.G
|
||||
|
||||
|
||||
21
core/base.py
21
core/base.py
@@ -1,6 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
from sqlite3 import Row
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BasePoint(BaseModel):
|
||||
@@ -27,6 +28,22 @@ class Proof(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class Invoice(BaseModel):
|
||||
amount: int
|
||||
pr: str
|
||||
hash: str
|
||||
issued: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row):
|
||||
return cls(
|
||||
amount=int(row[0]),
|
||||
pr=str(row[1]),
|
||||
hash=str(row[2]),
|
||||
issued=bool(row[3]),
|
||||
)
|
||||
|
||||
|
||||
class MintPayload(BaseModel):
|
||||
amount: int
|
||||
B_: BasePoint
|
||||
|
||||
@@ -9,7 +9,6 @@ from sqlalchemy import create_engine
|
||||
from sqlalchemy_aio.base import AsyncConnection
|
||||
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
|
||||
|
||||
|
||||
POSTGRES = "POSTGRES"
|
||||
COCKROACH = "COCKROACH"
|
||||
SQLITE = "SQLITE"
|
||||
|
||||
@@ -5,10 +5,15 @@ env.read_env()
|
||||
|
||||
DEBUG = env.bool("DEBUG", default=False)
|
||||
|
||||
MINT_PRIVATE_KEY = env.str("MINT_PRIVATE_KEY")
|
||||
|
||||
MINT_SERVER_HOST = env.str("MINT_SERVER_HOST", default="127.0.0.1")
|
||||
MINT_SERVER_PORT = env.int("MINT_SERVER_PORT", default=3338)
|
||||
|
||||
MINT_HOST = env.str("MINT_HOST", default="127.0.0.1")
|
||||
MINT_PORT = env.int("MINT_PORT", default=3338)
|
||||
|
||||
LNBITS_ENDPOINT = env.str("LNBITS_ENDPOINT", default=None)
|
||||
LNBITS_KEY = env.str("LNBITS_KEY", default=None)
|
||||
|
||||
MAX_ORDER = 64
|
||||
|
||||
@@ -6,22 +6,19 @@ from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
import requests
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Wallet,
|
||||
)
|
||||
from core.settings import LNBITS_ENDPOINT, LNBITS_KEY
|
||||
|
||||
from .base import (InvoiceResponse, PaymentResponse, PaymentStatus,
|
||||
StatusResponse, Wallet)
|
||||
|
||||
|
||||
class LNbitsWallet(Wallet):
|
||||
"""https://github.com/lnbits/lnbits"""
|
||||
|
||||
def __init__(self):
|
||||
self.endpoint = getenv("LNBITS_ENDPOINT")
|
||||
self.endpoint = LNBITS_ENDPOINT
|
||||
|
||||
key = getenv("LNBITS_KEY")
|
||||
key = LNBITS_KEY
|
||||
self.key = {"X-Api-Key": key}
|
||||
self.s = requests.Session()
|
||||
self.s.auth = ("user", "pass")
|
||||
|
||||
96
mint/app.py
96
mint/app.py
@@ -1,58 +1,23 @@
|
||||
from ecc.curve import secp256k1, Point
|
||||
from fastapi import FastAPI
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi.params import Depends, Query, Body
|
||||
|
||||
import sys
|
||||
import asyncio
|
||||
import logging
|
||||
import uvicorn
|
||||
import sys
|
||||
from ast import Param
|
||||
from typing import Union
|
||||
|
||||
from ecc.curve import Point, secp256k1
|
||||
from fastapi import FastAPI
|
||||
from fastapi.params import Body, Depends, Query
|
||||
from fastapi.routing import APIRouter
|
||||
from loguru import logger
|
||||
|
||||
import core.settings as settings
|
||||
from core.base import MintPayloads, SplitPayload
|
||||
from core.settings import MINT_PRIVATE_KEY
|
||||
from lightning import WALLET
|
||||
from mint.ledger import Ledger
|
||||
from mint.migrations import m001_initial
|
||||
from lightning import WALLET
|
||||
import core.settings as settings
|
||||
|
||||
from core.base import MintPayload, MintPayloads, SplitPayload
|
||||
|
||||
# from .app import create_app
|
||||
|
||||
|
||||
# Ledger pubkey
|
||||
ledger = Ledger("supersecretprivatekey", "data/mint")
|
||||
|
||||
|
||||
# class MyFlaskApp(Flask):
|
||||
# """
|
||||
# We overload the Flask class so we can run a startup script (migration).
|
||||
# Stupid Flask.
|
||||
# """
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# async def create_tasks_func():
|
||||
# await asyncio.wait([m001_initial(ledger.db)])
|
||||
# await ledger.load_used_proofs()
|
||||
|
||||
# error_message, balance = await WALLET.status()
|
||||
# if error_message:
|
||||
# print(
|
||||
# f"The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'",
|
||||
# RuntimeWarning,
|
||||
# )
|
||||
|
||||
# print(f"Lightning balance: {balance} sat")
|
||||
|
||||
# print("Mint started.")
|
||||
|
||||
# loop = asyncio.get_event_loop()
|
||||
# loop.run_until_complete(create_tasks_func())
|
||||
# loop.close()
|
||||
|
||||
# return super().__init__(*args, **kwargs)
|
||||
|
||||
# def run(self, *args, **options):
|
||||
# super(MyFlaskApp, self).run(*args, **options)
|
||||
ledger = Ledger(MINT_PRIVATE_KEY, "data/mint")
|
||||
|
||||
|
||||
def startup(app: FastAPI):
|
||||
@@ -124,18 +89,39 @@ def create_app(config_object="core.settings") -> FastAPI:
|
||||
app = create_app()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"Hello": "world"}
|
||||
|
||||
|
||||
@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):
|
||||
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.payloads:
|
||||
@@ -146,7 +132,7 @@ async def mint(payloads: MintPayloads):
|
||||
B_ = Point(x, y, secp256k1)
|
||||
B_s.append(B_)
|
||||
try:
|
||||
promises = await ledger.mint(B_s, amounts)
|
||||
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
|
||||
return promises
|
||||
except Exception as exc:
|
||||
return {"error": str(exc)}
|
||||
|
||||
51
mint/crud.py
51
mint/crud.py
@@ -1,5 +1,7 @@
|
||||
import secrets
|
||||
from typing import Optional
|
||||
|
||||
from core.base import Invoice
|
||||
from core.db import Connection, Database
|
||||
|
||||
|
||||
@@ -62,3 +64,52 @@ async def invalidate_proof(
|
||||
str(proof["secret"]),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def store_lightning_invoice(
|
||||
invoice: Invoice,
|
||||
db: Database,
|
||||
conn: Optional[Connection] = None,
|
||||
):
|
||||
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO invoices
|
||||
(amount, pr, hash, issued)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
invoice.amount,
|
||||
invoice.pr,
|
||||
invoice.hash,
|
||||
invoice.issued,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def get_lightning_invoice(
|
||||
hash: str,
|
||||
db: Database,
|
||||
conn: Optional[Connection] = None,
|
||||
):
|
||||
|
||||
row = await (conn or db).fetchone(
|
||||
"""
|
||||
SELECT * from invoices
|
||||
WHERE hash = ?
|
||||
""",
|
||||
hash,
|
||||
)
|
||||
return Invoice.from_row(row)
|
||||
|
||||
|
||||
async def update_lightning_invoice(
|
||||
hash: str,
|
||||
issued: bool,
|
||||
db: Database,
|
||||
conn: Optional[Connection] = None,
|
||||
):
|
||||
await (conn or db).execute(
|
||||
"UPDATE invoices SET issued = ? WHERE hash = ?",
|
||||
(issued, hash),
|
||||
)
|
||||
|
||||
@@ -5,15 +5,18 @@ Implementation of https://gist.github.com/phyro/935badc682057f418842c72961cf096c
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
from ecc.curve import secp256k1, Point
|
||||
from ecc.curve import Point, secp256k1
|
||||
from ecc.key import gen_keypair
|
||||
|
||||
import core.b_dhke as b_dhke
|
||||
from core.base import Invoice
|
||||
from core.db import Database
|
||||
from core.split import amount_split
|
||||
from core.settings import MAX_ORDER
|
||||
from mint.crud import store_promise, invalidate_proof, get_proofs_used
|
||||
from core.split import amount_split
|
||||
from lightning import WALLET
|
||||
from mint.crud import (get_lightning_invoice, get_proofs_used,
|
||||
invalidate_proof, store_lightning_invoice,
|
||||
store_promise, update_lightning_invoice)
|
||||
|
||||
|
||||
class Ledger:
|
||||
@@ -65,7 +68,7 @@ class Ledger:
|
||||
def _verify_proof(self, proof):
|
||||
"""Verifies that the proof of promise was issued by this ledger."""
|
||||
if proof["secret"] in self.proofs_used:
|
||||
raise Exception(f"Already spent. Secret: {proof['secret']}")
|
||||
raise Exception(f"tokens already spent. Secret: {proof['secret']}")
|
||||
secret_key = self.keys[proof["amount"]] # Get the correct key to check against
|
||||
C = Point(proof["C"]["x"], proof["C"]["y"], secp256k1)
|
||||
return b_dhke.verify(secret_key, C, proof["secret"])
|
||||
@@ -94,13 +97,13 @@ class Ledger:
|
||||
self._verify_amount(amount)
|
||||
except:
|
||||
# For better error message
|
||||
raise Exception("Invalid split amount: " + str(amount))
|
||||
raise Exception("invalid split amount: " + str(amount))
|
||||
|
||||
def _verify_amount(self, amount):
|
||||
"""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
|
||||
if not valid:
|
||||
raise Exception("Invalid amount: " + str(amount))
|
||||
raise Exception("invalid amount: " + str(amount))
|
||||
return amount
|
||||
|
||||
def _verify_equation_balanced(self, proofs, outs):
|
||||
@@ -119,42 +122,62 @@ class Ledger:
|
||||
rv.append(2**pos)
|
||||
return rv
|
||||
|
||||
async def _request_lightning(self, amount):
|
||||
async def _request_lightning_invoice(self, amount):
|
||||
error, balance = await WALLET.status()
|
||||
if error:
|
||||
raise Exception(f"Lightning wallet not responding: {error}")
|
||||
ok, checking_id, payment_request, error_message = await WALLET.create_invoice(
|
||||
amount, "cashu deposit"
|
||||
)
|
||||
print(payment_request)
|
||||
return payment_request, checking_id
|
||||
|
||||
timeout = time.time() + 60 # 1 minute to pay invoice
|
||||
while True:
|
||||
status = await WALLET.get_invoice_status(checking_id)
|
||||
if status.pending and time.time() > timeout:
|
||||
print("Timeout")
|
||||
return False
|
||||
if not status.pending:
|
||||
print("paid")
|
||||
return True
|
||||
time.sleep(5)
|
||||
async def _check_lightning_invoice(self, payment_hash):
|
||||
invoice: Invoice = await get_lightning_invoice(payment_hash, db=self.db)
|
||||
if invoice.issued:
|
||||
raise Exception("tokens already issued for this invoice")
|
||||
status = await WALLET.get_invoice_status(payment_hash)
|
||||
if status.paid:
|
||||
await update_lightning_invoice(payment_hash, issued=True, db=self.db)
|
||||
return status.paid
|
||||
|
||||
# async def _wait_for_lightning_invoice(self, amount):
|
||||
# timeout = time.time() + 60 # 1 minute to pay invoice
|
||||
# while True:
|
||||
# status = await WALLET.get_invoice_status(checking_id)
|
||||
# if status.pending and time.time() > timeout:
|
||||
# print("Timeout")
|
||||
# return False
|
||||
# if not status.pending:
|
||||
# print("paid")
|
||||
# return True
|
||||
# time.sleep(5)
|
||||
|
||||
# Public methods
|
||||
def get_pubkeys(self):
|
||||
"""Returns public keys for possible amounts."""
|
||||
return self.pub_keys
|
||||
|
||||
async def mint(self, B_s, amounts, lightning=False):
|
||||
async def request_mint(self, amount):
|
||||
"""Returns Lightning invoice and stores it in the db."""
|
||||
payment_request, checking_id = await self._request_lightning_invoice(amount)
|
||||
invoice = Invoice(
|
||||
amount=amount, pr=payment_request, hash=checking_id, issued=False
|
||||
)
|
||||
if not payment_request or not checking_id:
|
||||
raise Exception(f"Could not create Lightning invoice.")
|
||||
await store_lightning_invoice(invoice, db=self.db)
|
||||
return payment_request, checking_id
|
||||
|
||||
async def mint(self, B_s, amounts, payment_hash=None):
|
||||
"""Mints a promise for coins for B_."""
|
||||
# check if lightning invoice was paid
|
||||
if payment_hash and not await self._check_lightning_invoice(payment_hash):
|
||||
raise Exception("Lightning invoice not paid yet.")
|
||||
|
||||
for amount in amounts:
|
||||
if amount not in [2**i for i in range(MAX_ORDER)]:
|
||||
raise Exception(f"Can only mint amounts up to {2**MAX_ORDER}.")
|
||||
|
||||
if lightning:
|
||||
paid = await self._request_lightning(sum(amounts))
|
||||
if not paid:
|
||||
raise Exception(f"Did not receive payment in time.")
|
||||
|
||||
promises = []
|
||||
for B_, amount in zip(B_s, amounts):
|
||||
split = amount_split(amount)
|
||||
|
||||
@@ -33,6 +33,20 @@ async def m001_initial(db: Database):
|
||||
"""
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS invoices (
|
||||
amount INTEGER NOT NULL,
|
||||
pr TEXT NOT NULL,
|
||||
hash TEXT NOT NULL,
|
||||
issued BOOL NOT NULL,
|
||||
|
||||
UNIQUE (hash)
|
||||
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE VIEW IF NOT EXISTS balance_issued AS
|
||||
|
||||
20
poetry.lock
generated
20
poetry.lock
generated
@@ -237,6 +237,20 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.10.1"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1,<4.0"
|
||||
|
||||
[package.extras]
|
||||
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
|
||||
requirements_deprecated_finder = ["pipreqs", "pip-api"]
|
||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||
plugins = ["setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.1.1"
|
||||
@@ -641,7 +655,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "ed00e50dd88910eebcd5db652d99e722f77226f4f102e3bdd1032a4b1700dd99"
|
||||
content-hash = "6c846ea3f88e2a806b202709a65d5e825cd2e5c8ea42892b1731fdeb7c718124"
|
||||
|
||||
[metadata.files]
|
||||
anyio = [
|
||||
@@ -737,6 +751,10 @@ iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
isort = [
|
||||
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
|
||||
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
|
||||
]
|
||||
itsdangerous = [
|
||||
{file = "itsdangerous-2.1.1-py3-none-any.whl", hash = "sha256:935642cd4b987cdbee7210080004033af76306757ff8b4c0a506a4b6e06f02cf"},
|
||||
{file = "itsdangerous-2.1.1.tar.gz", hash = "sha256:7b7d3023cd35d9cb0c1fd91392f8c95c6fa02c59bf8ad64b8849be3401b95afb"},
|
||||
|
||||
@@ -34,6 +34,7 @@ loguru = "^0.6.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = {version = "^22.8.0", allow-prereleases = true}
|
||||
isort = "^5.10.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import asyncio
|
||||
|
||||
from core.helpers import async_unwrap
|
||||
from wallet.migrations import m001_initial
|
||||
from wallet.wallet import Wallet as Wallet1
|
||||
from wallet.wallet import Wallet as Wallet2
|
||||
from wallet.migrations import m001_initial
|
||||
|
||||
|
||||
SERVER_ENDPOINT = "http://localhost:3338"
|
||||
|
||||
@@ -56,7 +55,7 @@ async def run_test():
|
||||
# print(exc.args[0])
|
||||
await assert_err(
|
||||
wallet1.split(wallet1.proofs + proofs, 20),
|
||||
f"Error: Already spent. Secret: {proofs[0]['secret']}",
|
||||
f"Error: tokens already spent. Secret: {proofs[0]['secret']}",
|
||||
)
|
||||
assert wallet1.balance == 63 + 64
|
||||
wallet1.status()
|
||||
@@ -73,7 +72,7 @@ async def run_test():
|
||||
# Error: We try to double-spend and it fails
|
||||
await assert_err(
|
||||
wallet1.split([proofs[0]], 10),
|
||||
f"Error: Already spent. Secret: {proofs[0]['secret']}",
|
||||
f"Error: tokens already spent. Secret: {proofs[0]['secret']}",
|
||||
)
|
||||
|
||||
assert wallet1.balance == 63 + 64
|
||||
@@ -102,7 +101,7 @@ async def run_test():
|
||||
# Error: We try to double-spend and it fails
|
||||
await assert_err(
|
||||
wallet1.split(w1_snd_proofs, 5),
|
||||
f"Error: Already spent. Secret: {w1_snd_proofs[0]['secret']}",
|
||||
f"Error: tokens already spent. Secret: {w1_snd_proofs[0]['secret']}",
|
||||
)
|
||||
|
||||
assert wallet1.balance == 63 + 64 - 20
|
||||
@@ -113,7 +112,7 @@ async def run_test():
|
||||
|
||||
await assert_err(
|
||||
wallet1.split(w1_snd_proofs, -500),
|
||||
"Error: Invalid split amount: -500",
|
||||
"Error: invalid split amount: -500",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# from core.db import Database
|
||||
import sys
|
||||
|
||||
# db = Database("database", "data/wallet")
|
||||
sys.tracebacklimit = None
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import secrets
|
||||
from typing import Optional
|
||||
from core.db import Connection, Database
|
||||
|
||||
# from wallet import db
|
||||
from core.base import Proof
|
||||
from core.db import Connection, Database
|
||||
|
||||
|
||||
async def store_proof(
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import random
|
||||
import asyncio
|
||||
import random
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
from ecc.curve import secp256k1, Point
|
||||
from typing import List
|
||||
from core.base import Proof, BasePoint
|
||||
from ecc.curve import Point, secp256k1
|
||||
|
||||
import core.b_dhke as b_dhke
|
||||
from core.base import BasePoint, MintPayload, MintPayloads, Proof, SplitPayload
|
||||
from core.db import Database
|
||||
from core.split import amount_split
|
||||
|
||||
from wallet.crud import store_proof, invalidate_proof, get_proofs
|
||||
|
||||
from core.base import MintPayload, MintPayloads, SplitPayload
|
||||
from wallet.crud import get_proofs, invalidate_proof, store_proof
|
||||
|
||||
|
||||
class LedgerAPI:
|
||||
@@ -48,7 +45,12 @@ class LedgerAPI:
|
||||
proofs.append(proof.dict())
|
||||
return proofs
|
||||
|
||||
def mint(self, amounts):
|
||||
def request_mint(self, amount):
|
||||
"""Requests a mint from the server and returns Lightning invoice."""
|
||||
r = requests.get(self.url + "/mint", params={"amount": amount})
|
||||
return r.json()
|
||||
|
||||
def mint(self, amounts, payment_hash=None):
|
||||
"""Mints new coins and returns a proof of promise."""
|
||||
payloads: MintPayloads = MintPayloads()
|
||||
secrets = []
|
||||
@@ -61,13 +63,13 @@ class LedgerAPI:
|
||||
blinded_point = BasePoint(x=str(B_.x), y=str(B_.y))
|
||||
payload: MintPayload = MintPayload(amount=amount, B_=blinded_point)
|
||||
payloads.payloads.append(payload)
|
||||
|
||||
promises = requests.post(
|
||||
self.url + "/mint",
|
||||
json=payloads.dict(),
|
||||
params={"payment_hash": payment_hash},
|
||||
).json()
|
||||
if "detail" in promises:
|
||||
raise Exception("Error: {}".format(promises["detail"]))
|
||||
if "error" in promises:
|
||||
raise Exception("Error: {}".format(promises["error"]))
|
||||
return self._construct_proofs(promises, [(r, s) for r, s in zip(rs, secrets)])
|
||||
|
||||
def split(self, proofs, amount):
|
||||
@@ -122,9 +124,12 @@ class Wallet(LedgerAPI):
|
||||
for proof in proofs:
|
||||
await store_proof(proof, db=self.db)
|
||||
|
||||
async def mint(self, amount):
|
||||
async def request_mint(self, amount):
|
||||
return super().request_mint(amount)
|
||||
|
||||
async def mint(self, amount, payment_hash=None):
|
||||
split = amount_split(amount)
|
||||
proofs = super().mint(split)
|
||||
proofs = super().mint(split, payment_hash)
|
||||
if proofs == []:
|
||||
raise Exception("received no proofs")
|
||||
await self._store_proofs(proofs)
|
||||
@@ -154,7 +159,7 @@ class Wallet(LedgerAPI):
|
||||
try:
|
||||
await self.split(proofs, sum(p["amount"] for p in proofs))
|
||||
except Exception as exc:
|
||||
assert exc.args[0].startswith("Error: Already spent."), Exception(
|
||||
assert exc.args[0].startswith("Error: tokens already spent."), Exception(
|
||||
"invalidating unspent tokens"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user