mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-01-08 19:24:21 +01:00
works
This commit is contained in:
11
mint/app.py
11
mint/app.py
@@ -5,10 +5,11 @@ from typing import Union
|
||||
|
||||
import click
|
||||
import uvicorn
|
||||
from ecc.curve import Point, secp256k1
|
||||
from fastapi import FastAPI
|
||||
from loguru import logger
|
||||
|
||||
from secp256k1 import PublicKey
|
||||
|
||||
import core.settings as settings
|
||||
from core.base import MintPayloads, SplitPayload, MeltPayload, CheckPayload
|
||||
from core.settings import MINT_PRIVATE_KEY, MINT_SERVER_HOST, MINT_SERVER_PORT
|
||||
@@ -124,12 +125,8 @@ async def mint(payloads: MintPayloads, payment_hash: Union[str, None] = None):
|
||||
amounts = []
|
||||
B_s = []
|
||||
for payload in payloads.blinded_messages:
|
||||
v = payload.dict()
|
||||
amounts.append(v["amount"])
|
||||
x = int(v["B_"]["x"])
|
||||
y = int(v["B_"]["y"])
|
||||
B_ = Point(x, y, secp256k1)
|
||||
B_s.append(B_)
|
||||
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
|
||||
|
||||
23
mint/crud.py
23
mint/crud.py
@@ -7,10 +7,8 @@ from core.db import Connection, Database
|
||||
|
||||
async def store_promise(
|
||||
amount: int,
|
||||
B_x: str,
|
||||
B_y: str,
|
||||
C_x: str,
|
||||
C_y: str,
|
||||
B_: str,
|
||||
C_: str,
|
||||
db: Database,
|
||||
conn: Optional[Connection] = None,
|
||||
):
|
||||
@@ -18,15 +16,13 @@ async def store_promise(
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO promises
|
||||
(amount, B_x, B_y, C_x, C_y)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
(amount, B_b, C_b)
|
||||
VALUES (?, ?, ?)
|
||||
""",
|
||||
(
|
||||
amount,
|
||||
str(B_x),
|
||||
str(B_y),
|
||||
str(C_x),
|
||||
str(C_y),
|
||||
str(B_),
|
||||
str(C_),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -54,13 +50,12 @@ async def invalidate_proof(
|
||||
await (conn or db).execute(
|
||||
"""
|
||||
INSERT INTO proofs_used
|
||||
(amount, C_x, C_y, secret)
|
||||
VALUES (?, ?, ?, ?)
|
||||
(amount, C, secret)
|
||||
VALUES (?, ?, ?)
|
||||
""",
|
||||
(
|
||||
proof.amount,
|
||||
str(proof.C.x),
|
||||
str(proof.C.y),
|
||||
str(proof.C),
|
||||
str(proof.secret),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -3,12 +3,10 @@ Implementation of https://gist.github.com/phyro/935badc682057f418842c72961cf096c
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import math
|
||||
from ecc.curve import Point, secp256k1
|
||||
from ecc.key import gen_keypair
|
||||
from core.secp import PrivateKey, PublicKey
|
||||
|
||||
from typing import List
|
||||
from core.base import Proof, BlindedMessage, BlindedSignature, BasePoint
|
||||
from typing import List, Set
|
||||
from core.base import Proof, BlindedMessage, BlindedSignature
|
||||
|
||||
import core.b_dhke as b_dhke
|
||||
from core.base import Invoice
|
||||
@@ -30,49 +28,49 @@ from mint.crud import (
|
||||
|
||||
class Ledger:
|
||||
def __init__(self, secret_key: str, db: str):
|
||||
self.proofs_used = set()
|
||||
self.proofs_used: Set[str] = set()
|
||||
|
||||
self.master_key = secret_key
|
||||
self.keys = self._derive_keys(self.master_key)
|
||||
self.pub_keys = self._derive_pubkeys(self.keys)
|
||||
self.db = Database("mint", db)
|
||||
self.master_key: str = secret_key
|
||||
self.keys: List[PrivateKey] = self._derive_keys(self.master_key)
|
||||
self.pub_keys: List[PublicKey] = self._derive_pubkeys(self.keys)
|
||||
self.db: Database = Database("mint", db)
|
||||
|
||||
async def load_used_proofs(self):
|
||||
self.proofs_used = set(await get_proofs_used(db=self.db))
|
||||
|
||||
@staticmethod
|
||||
def _derive_keys(master_key):
|
||||
def _derive_keys(master_key: str):
|
||||
"""Deterministic derivation of keys for 2^n values."""
|
||||
return {
|
||||
2
|
||||
** i: int(
|
||||
** i: PrivateKey(
|
||||
hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
|
||||
.hexdigest()
|
||||
.encode("utf-8"),
|
||||
16,
|
||||
.encode("utf-8")[:32],
|
||||
raw=True,
|
||||
)
|
||||
for i in range(MAX_ORDER)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _derive_pubkeys(keys):
|
||||
return {
|
||||
amt: keys[amt] * secp256k1.G for amt in [2**i for i in range(MAX_ORDER)]
|
||||
}
|
||||
def _derive_pubkeys(keys: List[PrivateKey]):
|
||||
return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
|
||||
|
||||
async def _generate_promises(self, amounts, B_s):
|
||||
"""Generates promises that sum to the given amount."""
|
||||
return [
|
||||
await self._generate_promise(amount, Point(B_.x, B_.y, secp256k1))
|
||||
await self._generate_promise(amount, PublicKey(bytes.fromhex(B_), raw=True))
|
||||
for (amount, B_) in zip(amounts, B_s)
|
||||
]
|
||||
|
||||
async def _generate_promise(self, amount: int, B_):
|
||||
async def _generate_promise(self, amount: int, B_: PublicKey):
|
||||
"""Generates a promise for given amount and returns a pair (amount, C')."""
|
||||
secret_key = self.keys[amount] # Get the correct key
|
||||
C_ = b_dhke.step2_alice(B_, secret_key)
|
||||
await store_promise(amount, B_x=B_.x, B_y=B_.y, C_x=C_.x, C_y=C_.y, db=self.db)
|
||||
return BlindedSignature(amount=amount, C_=BasePoint(x=C_.x, y=C_.y))
|
||||
await store_promise(
|
||||
amount, B_=B_.serialize().hex(), C_=C_.serialize().hex(), db=self.db
|
||||
)
|
||||
return BlindedSignature(amount=amount, C_=C_.serialize().hex())
|
||||
|
||||
def _check_spendable(self, proof: Proof):
|
||||
"""Checks whether the proof was already spent."""
|
||||
@@ -83,10 +81,12 @@ class Ledger:
|
||||
if not self._check_spendable(proof):
|
||||
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)
|
||||
C = PublicKey(bytes.fromhex(proof.C), raw=True)
|
||||
return b_dhke.verify(secret_key, C, proof.secret)
|
||||
|
||||
def _verify_outputs(self, total: int, amount: int, output_data):
|
||||
def _verify_outputs(
|
||||
self, total: int, amount: int, output_data: List[BlindedMessage]
|
||||
):
|
||||
"""Verifies the expected split was correctly computed"""
|
||||
fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
|
||||
fst_outputs = amount_split(fst_amt)
|
||||
@@ -95,16 +95,18 @@ class Ledger:
|
||||
given = [o.amount for o in output_data]
|
||||
return given == expected
|
||||
|
||||
def _verify_no_duplicates(self, proofs: List[Proof], output_data):
|
||||
def _verify_no_duplicates(
|
||||
self, proofs: List[Proof], output_data: List[BlindedMessage]
|
||||
):
|
||||
secrets = [p.secret for p in proofs]
|
||||
if len(secrets) != len(list(set(secrets))):
|
||||
return False
|
||||
B_xs = [od.B_.x for od in output_data]
|
||||
if len(B_xs) != len(list(set(B_xs))):
|
||||
B_s = [od.B_ for od in output_data]
|
||||
if len(B_s) != len(list(set(B_s))):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _verify_split_amount(self, amount):
|
||||
def _verify_split_amount(self, amount: int):
|
||||
"""Split amount like output amount can't be negative or too big."""
|
||||
try:
|
||||
self._verify_amount(amount)
|
||||
@@ -179,7 +181,7 @@ class Ledger:
|
||||
# Public methods
|
||||
def get_pubkeys(self):
|
||||
"""Returns public keys for possible amounts."""
|
||||
return self.pub_keys
|
||||
return {a: p.serialize().hex() for a, p in self.pub_keys.items()}
|
||||
|
||||
async def request_mint(self, amount):
|
||||
"""Returns Lightning invoice and stores it in the db."""
|
||||
@@ -239,7 +241,7 @@ class Ledger:
|
||||
if not all([self._verify_proof(p) for p in proofs]):
|
||||
return False
|
||||
|
||||
total = sum([p["amount"] for p in proofs])
|
||||
total = sum([p.amount for p in proofs])
|
||||
|
||||
if not self._verify_no_duplicates(proofs, output_data):
|
||||
raise Exception("duplicate proofs or promises")
|
||||
|
||||
@@ -17,12 +17,10 @@ async def m001_initial(db: Database):
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS promises (
|
||||
amount INTEGER NOT NULL,
|
||||
B_x TEXT NOT NULL,
|
||||
B_y TEXT NOT NULL,
|
||||
C_x TEXT NOT NULL,
|
||||
C_y TEXT NOT NULL,
|
||||
B_b TEXT NOT NULL,
|
||||
C_b TEXT NOT NULL,
|
||||
|
||||
UNIQUE (B_x, B_y)
|
||||
UNIQUE (B_b)
|
||||
|
||||
);
|
||||
"""
|
||||
@@ -32,8 +30,7 @@ async def m001_initial(db: Database):
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS proofs_used (
|
||||
amount INTEGER NOT NULL,
|
||||
C_x TEXT NOT NULL,
|
||||
C_y TEXT NOT NULL,
|
||||
C TEXT NOT NULL,
|
||||
secret TEXT NOT NULL,
|
||||
|
||||
UNIQUE (secret)
|
||||
|
||||
Reference in New Issue
Block a user