mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-21 19:14:19 +01:00
Merge pull request #33 from callebtc/fix/hash_to_curve
Fix/hash to curve
This commit is contained in:
@@ -6,7 +6,7 @@ Alice:
|
|||||||
A = a*G
|
A = a*G
|
||||||
return A
|
return A
|
||||||
Bob:
|
Bob:
|
||||||
Y = hash_to_point(secret_message)
|
Y = hash_to_curve(secret_message)
|
||||||
r = random blinding factor
|
r = random blinding factor
|
||||||
B'= Y + r*G
|
B'= Y + r*G
|
||||||
return B'
|
return B'
|
||||||
@@ -20,7 +20,7 @@ C = C' - r*A
|
|||||||
(= a*Y)
|
(= a*Y)
|
||||||
return C, secret_message
|
return C, secret_message
|
||||||
Alice:
|
Alice:
|
||||||
Y = hash_to_point(secret_message)
|
Y = hash_to_curve(secret_message)
|
||||||
C == a*Y
|
C == a*Y
|
||||||
If true, C must have originated from Alice
|
If true, C must have originated from Alice
|
||||||
"""
|
"""
|
||||||
@@ -30,28 +30,22 @@ import hashlib
|
|||||||
from secp256k1 import PrivateKey, PublicKey
|
from secp256k1 import PrivateKey, PublicKey
|
||||||
|
|
||||||
|
|
||||||
def hash_to_point(secret_msg):
|
def hash_to_curve(message: bytes):
|
||||||
"""Generates x coordinate from the message hash and checks if the point lies on the curve.
|
"""Generates a point from the message hash and checks if the point lies on the curve.
|
||||||
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
|
If it does not, it tries computing a new point from the hash."""
|
||||||
point = None
|
point = None
|
||||||
msg = secret_msg
|
msg_to_hash = message
|
||||||
while point is None:
|
while point is None:
|
||||||
_hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
|
|
||||||
try:
|
try:
|
||||||
# We construct compressed pub which has x coordinate encoded with even y
|
_hash = hashlib.sha256(msg_to_hash).digest()
|
||||||
_hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
|
point = PublicKey(b"\x02" + _hash, raw=True)
|
||||||
_hash[0] = 0x02 # set first byte to represent even y coord
|
|
||||||
_hash = bytes(_hash)
|
|
||||||
point = PublicKey(_hash, raw=True)
|
|
||||||
except:
|
except:
|
||||||
msg = _hash
|
msg_to_hash = _hash
|
||||||
|
|
||||||
return point
|
return point
|
||||||
|
|
||||||
|
|
||||||
def step1_alice(secret_msg):
|
def step1_alice(secret_msg: str):
|
||||||
secret_msg = secret_msg.encode("utf-8")
|
Y = hash_to_curve(secret_msg.encode("utf-8"))
|
||||||
Y = hash_to_point(secret_msg)
|
|
||||||
r = PrivateKey()
|
r = PrivateKey()
|
||||||
B_ = Y + r.pubkey
|
B_ = Y + r.pubkey
|
||||||
return B_, r
|
return B_, r
|
||||||
@@ -68,7 +62,7 @@ def step3_alice(C_, r, A):
|
|||||||
|
|
||||||
|
|
||||||
def verify(a, C, secret_msg):
|
def verify(a, C, secret_msg):
|
||||||
Y = hash_to_point(secret_msg.encode("utf-8"))
|
Y = hash_to_curve(secret_msg.encode("utf-8"))
|
||||||
return C == Y.mult(a)
|
return C == Y.mult(a)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ class MintKeyset:
|
|||||||
first_seen=None,
|
first_seen=None,
|
||||||
active=None,
|
active=None,
|
||||||
seed: Union[None, str] = None,
|
seed: Union[None, str] = None,
|
||||||
derivation_path: str = "0",
|
derivation_path: str = None,
|
||||||
):
|
):
|
||||||
self.derivation_path = derivation_path
|
self.derivation_path = derivation_path
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|||||||
27
cashu/core/legacy.py
Normal file
27
cashu/core/legacy.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import hashlib
|
||||||
|
|
||||||
|
from secp256k1 import PublicKey
|
||||||
|
|
||||||
|
|
||||||
|
def hash_to_point_pre_0_3_3(secret_msg):
|
||||||
|
"""Generates x coordinate from the message hash and checks if the point lies on the curve.
|
||||||
|
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
|
||||||
|
point = None
|
||||||
|
msg = secret_msg
|
||||||
|
while point is None:
|
||||||
|
_hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
|
||||||
|
try:
|
||||||
|
# We construct compressed pub which has x coordinate encoded with even y
|
||||||
|
_hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
|
||||||
|
_hash[0] = 0x02 # set first byte to represent even y coord
|
||||||
|
_hash = bytes(_hash)
|
||||||
|
point = PublicKey(_hash, raw=True)
|
||||||
|
except:
|
||||||
|
msg = _hash
|
||||||
|
|
||||||
|
return point
|
||||||
|
|
||||||
|
|
||||||
|
def verify_pre_0_3_3(a, C, secret_msg):
|
||||||
|
Y = hash_to_point_pre_0_3_3(secret_msg.encode("utf-8"))
|
||||||
|
return C == Y.mult(a)
|
||||||
@@ -48,4 +48,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.3.2"
|
VERSION = "0.3.3"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from cashu.core.settings import MINT_PRIVATE_KEY
|
from cashu.core.settings import MINT_PRIVATE_KEY
|
||||||
from cashu.mint.ledger import Ledger
|
from cashu.mint.ledger import Ledger
|
||||||
|
|
||||||
ledger = Ledger(MINT_PRIVATE_KEY, "data/mint")
|
ledger = Ledger(MINT_PRIVATE_KEY, "data/mint", derivation_path="0/0/0/0")
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
import asyncio
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from starlette.middleware import Middleware
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
from starlette_context import context
|
||||||
|
from starlette_context.middleware import RawContextMiddleware
|
||||||
|
|
||||||
from cashu.core.settings import DEBUG, VERSION
|
from cashu.core.settings import DEBUG, VERSION
|
||||||
from cashu.lightning import WALLET
|
|
||||||
from cashu.mint.migrations import m001_initial
|
|
||||||
|
|
||||||
from . import ledger
|
|
||||||
from .router import router
|
from .router import router
|
||||||
from .startup import load_ledger
|
from .startup import load_ledger
|
||||||
|
|
||||||
|
|
||||||
|
class CustomHeaderMiddleware(BaseHTTPMiddleware):
|
||||||
|
"""
|
||||||
|
Middleware for starlette that can set the context from request headers
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def dispatch(self, request, call_next):
|
||||||
|
context["client-version"] = request.headers.get("Client-version")
|
||||||
|
response = await call_next(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_object="core.settings") -> FastAPI:
|
def create_app(config_object="core.settings") -> FastAPI:
|
||||||
def configure_logger() -> None:
|
def configure_logger() -> None:
|
||||||
class Formatter:
|
class Formatter:
|
||||||
@@ -49,6 +60,13 @@ def create_app(config_object="core.settings") -> FastAPI:
|
|||||||
|
|
||||||
configure_logger()
|
configure_logger()
|
||||||
|
|
||||||
|
middleware = [
|
||||||
|
Middleware(
|
||||||
|
RawContextMiddleware,
|
||||||
|
),
|
||||||
|
Middleware(CustomHeaderMiddleware),
|
||||||
|
]
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Cashu Mint",
|
title="Cashu Mint",
|
||||||
description="Ecash wallet and mint with Bitcoin Lightning support.",
|
description="Ecash wallet and mint with Bitcoin Lightning support.",
|
||||||
@@ -57,8 +75,8 @@ def create_app(config_object="core.settings") -> FastAPI:
|
|||||||
"name": "MIT License",
|
"name": "MIT License",
|
||||||
"url": "https://raw.githubusercontent.com/callebtc/cashu/main/LICENSE",
|
"url": "https://raw.githubusercontent.com/callebtc/cashu/main/LICENSE",
|
||||||
},
|
},
|
||||||
|
middleware=middleware,
|
||||||
)
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import math
|
|||||||
from typing import Dict, List, Set
|
from typing import Dict, List, Set
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from starlette_context import context
|
||||||
|
|
||||||
import cashu.core.b_dhke as b_dhke
|
import cashu.core.b_dhke as b_dhke
|
||||||
import cashu.core.bolt11 as bolt11
|
import cashu.core.bolt11 as bolt11
|
||||||
|
import cashu.core.legacy as legacy
|
||||||
from cashu.core.base import (
|
from cashu.core.base import (
|
||||||
BlindedMessage,
|
BlindedMessage,
|
||||||
BlindedSignature,
|
BlindedSignature,
|
||||||
@@ -55,11 +57,12 @@ class Ledger:
|
|||||||
seed=self.master_key, derivation_path=self.derivation_path
|
seed=self.master_key, derivation_path=self.derivation_path
|
||||||
)
|
)
|
||||||
# check if current keyset is stored in db and store if not
|
# check if current keyset is stored in db and store if not
|
||||||
|
logger.debug(f"Loading keyset {self.keyset.id} from db.")
|
||||||
current_keyset_local: List[MintKeyset] = await get_keyset(
|
current_keyset_local: List[MintKeyset] = await get_keyset(
|
||||||
id=self.keyset.id, db=self.db
|
id=self.keyset.id, db=self.db
|
||||||
)
|
)
|
||||||
if not len(current_keyset_local):
|
if not len(current_keyset_local):
|
||||||
logger.debug(f"Storing keyset {self.keyset.id}")
|
logger.debug(f"Storing keyset {self.keyset.id}.")
|
||||||
await store_keyset(keyset=self.keyset, db=self.db)
|
await store_keyset(keyset=self.keyset, db=self.db)
|
||||||
|
|
||||||
# load all past keysets from db
|
# load all past keysets from db
|
||||||
@@ -112,6 +115,11 @@ class Ledger:
|
|||||||
secret_key = self.keysets.keysets[proof.id].private_keys[proof.amount]
|
secret_key = self.keysets.keysets[proof.id].private_keys[proof.amount]
|
||||||
|
|
||||||
C = PublicKey(bytes.fromhex(proof.C), raw=True)
|
C = PublicKey(bytes.fromhex(proof.C), raw=True)
|
||||||
|
|
||||||
|
# backwards compatibility with old hash_to_curve
|
||||||
|
# old clients do not send a version
|
||||||
|
if not context.get("client-version"):
|
||||||
|
return legacy.verify_pre_0_3_3(secret_key, C, proof.secret)
|
||||||
return b_dhke.verify(secret_key, C, proof.secret)
|
return b_dhke.verify(secret_key, C, proof.secret)
|
||||||
|
|
||||||
def _verify_script(self, idx: int, proof: Proof):
|
def _verify_script(self, idx: int, proof: Proof):
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ from cashu.mint import ledger
|
|||||||
router: APIRouter = APIRouter()
|
router: APIRouter = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette_context import context
|
||||||
|
|
||||||
|
|
||||||
@router.get("/keys")
|
@router.get("/keys")
|
||||||
def keys():
|
def keys():
|
||||||
"""Get the public keys of the mint"""
|
"""Get the public keys of the mint"""
|
||||||
@@ -49,7 +53,6 @@ async def request_mint(amount: int = 0):
|
|||||||
@router.post("/mint")
|
@router.post("/mint")
|
||||||
async def mint(
|
async def mint(
|
||||||
payloads: MintRequest,
|
payloads: MintRequest,
|
||||||
bolt11: Union[str, None] = None,
|
|
||||||
payment_hash: Union[str, None] = None,
|
payment_hash: Union[str, None] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -70,7 +73,7 @@ async def mint(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/melt")
|
@router.post("/melt")
|
||||||
async def melt(payload: MeltRequest):
|
async def melt(request: Request, payload: MeltRequest):
|
||||||
"""
|
"""
|
||||||
Requests tokens to be destroyed and sent out via Lightning.
|
Requests tokens to be destroyed and sent out via Lightning.
|
||||||
"""
|
"""
|
||||||
@@ -97,7 +100,7 @@ async def check_fees(payload: CheckFeesRequest):
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/split")
|
@router.post("/split")
|
||||||
async def split(payload: SplitRequest):
|
async def split(request: Request, payload: SplitRequest):
|
||||||
"""
|
"""
|
||||||
Requetst a set of tokens with amount "total" to be split into two
|
Requetst a set of tokens with amount "total" to be split into two
|
||||||
newly minted sets with amount "split" and "total-split".
|
newly minted sets with amount "split" and "total-split".
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from cashu.core.script import (
|
|||||||
step2_carol_sign_tx,
|
step2_carol_sign_tx,
|
||||||
)
|
)
|
||||||
from cashu.core.secp import PublicKey
|
from cashu.core.secp import PublicKey
|
||||||
from cashu.core.settings import DEBUG
|
from cashu.core.settings import DEBUG, VERSION
|
||||||
from cashu.core.split import amount_split
|
from cashu.core.split import amount_split
|
||||||
from cashu.wallet.crud import (
|
from cashu.wallet.crud import (
|
||||||
get_keyset,
|
get_keyset,
|
||||||
@@ -52,7 +52,10 @@ class LedgerAPI:
|
|||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
async def _get_keys(self, url):
|
async def _get_keys(self, url):
|
||||||
resp = requests.get(url + "/keys").json()
|
resp = requests.get(
|
||||||
|
url + "/keys",
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
|
).json()
|
||||||
keys = resp
|
keys = resp
|
||||||
assert len(keys), Exception("did not receive any keys")
|
assert len(keys), Exception("did not receive any keys")
|
||||||
keyset_keys = {
|
keyset_keys = {
|
||||||
@@ -63,7 +66,10 @@ class LedgerAPI:
|
|||||||
return keyset
|
return keyset
|
||||||
|
|
||||||
async def _get_keysets(self, url):
|
async def _get_keysets(self, url):
|
||||||
keysets = requests.get(url + "/keysets").json()
|
keysets = requests.get(
|
||||||
|
url + "/keysets",
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
|
).json()
|
||||||
assert len(keysets), Exception("did not receive any keysets")
|
assert len(keysets), Exception("did not receive any keysets")
|
||||||
return keysets
|
return keysets
|
||||||
|
|
||||||
@@ -177,6 +183,7 @@ class LedgerAPI:
|
|||||||
self.url + "/mint",
|
self.url + "/mint",
|
||||||
json=payloads.dict(),
|
json=payloads.dict(),
|
||||||
params={"payment_hash": payment_hash},
|
params={"payment_hash": payment_hash},
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
try:
|
try:
|
||||||
@@ -235,6 +242,7 @@ class LedgerAPI:
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
self.url + "/split",
|
self.url + "/split",
|
||||||
json=split_payload.dict(include=_splitrequest_include_fields(proofs)),
|
json=split_payload.dict(include=_splitrequest_include_fields(proofs)),
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
try:
|
try:
|
||||||
@@ -260,6 +268,7 @@ class LedgerAPI:
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
self.url + "/check",
|
self.url + "/check",
|
||||||
json=payload.dict(),
|
json=payload.dict(),
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return_dict = resp.json()
|
return_dict = resp.json()
|
||||||
@@ -272,6 +281,7 @@ class LedgerAPI:
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
self.url + "/checkfees",
|
self.url + "/checkfees",
|
||||||
json=payload.dict(),
|
json=payload.dict(),
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|
||||||
@@ -293,6 +303,7 @@ class LedgerAPI:
|
|||||||
resp = requests.post(
|
resp = requests.post(
|
||||||
self.url + "/melt",
|
self.url + "/melt",
|
||||||
json=payload.dict(include=_meltequest_include_fields(proofs)),
|
json=payload.dict(include=_meltequest_include_fields(proofs)),
|
||||||
|
headers={"Client-version": VERSION},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ Mint: `Bob`
|
|||||||
|
|
||||||
# Blind Diffie-Hellmann key exchange (BDH)
|
# Blind Diffie-Hellmann key exchange (BDH)
|
||||||
- Mint `Bob` publishes `K = kG`
|
- Mint `Bob` publishes `K = kG`
|
||||||
- `Alice` picks secret `x` and computes `Y = hash_to_point(x)`
|
- `Alice` picks secret `x` and computes `Y = hash_to_curve(x)`
|
||||||
- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce
|
- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce
|
||||||
- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange)
|
- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange)
|
||||||
- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z`
|
- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z`
|
||||||
- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`.
|
- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`.
|
||||||
- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_point(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets.
|
- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_curve(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets.
|
||||||
@@ -17,12 +17,12 @@ Mint: `Bob`
|
|||||||
|
|
||||||
# Blind Diffie-Hellmann key exchange (BDH)
|
# Blind Diffie-Hellmann key exchange (BDH)
|
||||||
- Mint `Bob` publishes `K = kG`
|
- Mint `Bob` publishes `K = kG`
|
||||||
- `Alice` picks secret `x` and computes `Y = hash_to_point(x)`
|
- `Alice` picks secret `x` and computes `Y = hash_to_curve(x)`
|
||||||
- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce
|
- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce
|
||||||
- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange)
|
- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange)
|
||||||
- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z`
|
- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z`
|
||||||
- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`.
|
- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`.
|
||||||
- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_point(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets.
|
- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_curve(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets.
|
||||||
|
|
||||||
# Cashu client protocol
|
# Cashu client protocol
|
||||||
|
|
||||||
|
|||||||
21
poetry.lock
generated
21
poetry.lock
generated
@@ -167,8 +167,8 @@ starlette = "0.19.1"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "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)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
|
all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "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)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
|
||||||
dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
|
dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.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.2)", "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.3,<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]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
@@ -553,6 +553,17 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
|
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "starlette-context"
|
||||||
|
version = "0.3.4"
|
||||||
|
description = "Access context in Starlette"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
starlette = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -632,7 +643,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 = "b4e980ee90226bab07750b1becc8c69df7752f6d168d200a79c782aa1efe61da"
|
content-hash = "14ff9c57ca971c645f1a075b5c6fa0a84a38eaf6399d14afa724136728a3da03"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
anyio = [
|
anyio = [
|
||||||
@@ -999,6 +1010,10 @@ starlette = [
|
|||||||
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
|
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
|
||||||
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
|
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
|
||||||
]
|
]
|
||||||
|
starlette-context = [
|
||||||
|
{file = "starlette_context-0.3.4-py37-none-any.whl", hash = "sha256:b16bf17bd3ead7ded2f458aebf7f913744b9cf28305e16c69b435a6c6ddf1135"},
|
||||||
|
{file = "starlette_context-0.3.4.tar.gz", hash = "sha256:2d28e1838302fb5d5adacadc10fb73fb2d5cca1f0aa1e279698701cc96f1567c"},
|
||||||
|
]
|
||||||
tomli = [
|
tomli = [
|
||||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "cashu"
|
name = "cashu"
|
||||||
version = "0.3.2"
|
version = "0.3.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"
|
||||||
@@ -22,6 +22,7 @@ bitstring = "^3.1.9"
|
|||||||
secp256k1 = "^0.14.0"
|
secp256k1 = "^0.14.0"
|
||||||
sqlalchemy-aio = "^0.17.0"
|
sqlalchemy-aio = "^0.17.0"
|
||||||
python-bitcoinlib = "^0.11.2"
|
python-bitcoinlib = "^0.11.2"
|
||||||
|
starlette-context = "^0.3.4"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = {version = "^22.8.0", allow-prereleases = true}
|
black = {version = "^22.8.0", allow-prereleases = true}
|
||||||
|
|||||||
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.3.2",
|
version="0.3.3",
|
||||||
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",
|
||||||
|
|||||||
Reference in New Issue
Block a user