This commit is contained in:
callebtc
2022-09-17 11:15:29 +03:00
parent be326fcd33
commit aa265dc457
10 changed files with 157 additions and 127 deletions

View File

@@ -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

View File

@@ -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),
),
)

View File

@@ -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")

View File

@@ -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)