Merge pull request #9 from callebtc/dep/libsecp256k1

Dep/libsecp256k1
This commit is contained in:
calle
2022-09-17 11:21:39 +03:00
committed by GitHub
12 changed files with 154 additions and 175 deletions

View File

@@ -1,40 +1,32 @@
# Don't trust me with cryptography.
""" """
Implementation of https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406 Implementation of https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406
Alice: Alice:
A = a*G A = a*G
return A return A
Bob: Bob:
Y = hash_to_curve(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'
Alice: Alice:
C' = a*B' C' = a*B'
(= a*Y + a*r*G) (= a*Y + a*r*G)
return C' return C'
Bob: Bob:
C = C' - r*A C = C' - r*A
(= C' - a*r*G) (= C' - a*r*G)
(= a*Y) (= a*Y)
return C, secret_message return C, secret_message
Alice: Alice:
Y = hash_to_curve(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
""" """
import hashlib import hashlib
from secp256k1 import PrivateKey, PublicKey
from ecc.curve import Point, secp256k1
from ecc.key import gen_keypair
G = secp256k1.G
def hash_to_curve(secret_msg): def hash_to_curve(secret_msg):
@@ -43,13 +35,15 @@ def hash_to_curve(secret_msg):
point = None point = None
msg = secret_msg msg = secret_msg
while point is None: while point is None:
x_coord = int(hashlib.sha256(msg).hexdigest().encode("utf-8"), 16) _hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
y_coord = secp256k1.compute_y(x_coord)
try: try:
# Fails if the point is not on the curve # We construct compressed pub which has x coordinate encoded with even y
point = Point(x_coord, y_coord, secp256k1) _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: except:
msg = str(x_coord).encode("utf-8") msg = _hash
return point return point
@@ -57,35 +51,42 @@ def hash_to_curve(secret_msg):
def step1_bob(secret_msg): def step1_bob(secret_msg):
secret_msg = secret_msg.encode("utf-8") secret_msg = secret_msg.encode("utf-8")
Y = hash_to_curve(secret_msg) Y = hash_to_curve(secret_msg)
r, _ = gen_keypair(secp256k1) r = PrivateKey()
B_ = Y + r * G B_ = Y + r.pubkey
return B_, r return B_, r
def step2_alice(B_, a): def step2_alice(B_, a):
C_ = a * B_ C_ = B_.mult(a)
return C_ return C_
def step3_bob(C_, r, A): def step3_bob(C_, r, A):
C = C_ - r * A C = C_ - A.mult(r)
return C return C
def verify(a, C, secret_msg): def verify(a, C, secret_msg):
Y = hash_to_curve(secret_msg.encode("utf-8")) Y = hash_to_curve(secret_msg.encode("utf-8"))
return C == a * Y return C == Y.mult(a)
### Below is a test of a simple positive and negative case ### Below is a test of a simple positive and negative case
# # Alice private key # # Alice's keys
# a, A = gen_keypair(secp256k1) # a = PrivateKey()
# A = a.pubkey
# secret_msg = "test" # secret_msg = "test"
# B_, r = step1_bob(secret_msg) # B_, r = step1_bob(secret_msg)
# C_ = step2_alice(B_, a) # C_ = step2_alice(B_, a)
# C = step3_bob(C_, r, A) # C = step3_bob(C_, r, A)
# print("C:{}, secret_msg:{}".format(C, secret_msg)) # print("C:{}, secret_msg:{}".format(C, secret_msg))
# assert verify(a, C, secret_msg) # assert verify(a, C, secret_msg)
# assert verify(a, C + 1*G, secret_msg) == False # adding 1*G shouldn't pass # assert verify(a, C + C, secret_msg) == False # adding C twice shouldn't pass
# assert verify(a, A, secret_msg) == False # A shouldn't pass
# # Test operations
# b = PrivateKey()
# B = b.pubkey
# assert -A -A + A == -A # neg
# assert B.mult(a) == A.mult(b) # a*B = A*b

View File

@@ -4,16 +4,9 @@ from typing import List
from pydantic import BaseModel from pydantic import BaseModel
class BasePoint(BaseModel):
"""Named BasePoint because it conflicts with ecc.curve.Point"""
x: int
y: int
class Proof(BaseModel): class Proof(BaseModel):
amount: int amount: int
C: BasePoint C: str
secret: str secret: str
reserved: bool = False # whether this proof is reserved for sending reserved: bool = False # whether this proof is reserved for sending
@@ -21,22 +14,16 @@ class Proof(BaseModel):
def from_row(cls, row: Row): def from_row(cls, row: Row):
return cls( return cls(
amount=row[0], amount=row[0],
C=dict( C=row[1],
x=int(row[1]), secret=row[2],
y=int(row[2]), reserved=row[3] or False,
),
secret=row[3],
reserved=row[4] or False,
) )
@classmethod @classmethod
def from_dict(cls, d: dict): def from_dict(cls, d: dict):
return cls( return cls(
amount=d["amount"], amount=d["amount"],
C=dict( C=d["C"],
x=int(d["C"]["x"]),
y=int(d["C"]["y"]),
),
secret=d["secret"], secret=d["secret"],
reserved=d["reserved"] or False, reserved=d["reserved"] or False,
) )
@@ -72,21 +59,18 @@ class Invoice(BaseModel):
class BlindedMessage(BaseModel): class BlindedMessage(BaseModel):
amount: int amount: int
B_: BasePoint B_: str
class BlindedSignature(BaseModel): class BlindedSignature(BaseModel):
amount: int amount: int
C_: BasePoint C_: str
@classmethod @classmethod
def from_dict(cls, d: dict): def from_dict(cls, d: dict):
return cls( return cls(
amount=d["amount"], amount=d["amount"],
C_=dict( C_=d["C_"],
x=int(d["C_"]["x"]),
y=int(d["C_"]["y"]),
),
) )

52
core/secp.py Normal file
View File

@@ -0,0 +1,52 @@
from secp256k1 import PrivateKey, PublicKey
# We extend the public key to define some operations on points
# Picked from https://github.com/WTRMQDev/secp256k1-zkp-py/blob/master/secp256k1_zkp/__init__.py
class PublicKeyExt(PublicKey):
def __add__(self, pubkey2):
if isinstance(pubkey2, PublicKey):
new_pub = PublicKey()
new_pub.combine([self.public_key, pubkey2.public_key])
return new_pub
else:
raise TypeError("Cant add pubkey and %s" % pubkey2.__class__)
def __neg__(self):
serialized = self.serialize()
first_byte, remainder = serialized[:1], serialized[1:]
# flip odd/even byte
first_byte = {b"\x03": b"\x02", b"\x02": b"\x03"}[first_byte]
return PublicKey(first_byte + remainder, raw=True)
def __sub__(self, pubkey2):
if isinstance(pubkey2, PublicKey):
return self + (-pubkey2)
else:
raise TypeError("Can't add pubkey and %s" % pubkey2.__class__)
def mult(self, privkey):
if isinstance(privkey, PrivateKey):
return self.tweak_mul(privkey.private_key)
else:
raise TypeError("Can't multiply with non privatekey")
def __eq__(self, pubkey2):
if isinstance(pubkey2, PublicKey):
seq1 = self.to_data()
seq2 = pubkey2.to_data()
return seq1 == seq2
else:
raise TypeError("Can't compare pubkey and %s" % pubkey2.__class__)
def to_data(self):
return [self.public_key.data[i] for i in range(64)]
# Horrible monkeypatching
PublicKey.__add__ = PublicKeyExt.__add__
PublicKey.__neg__ = PublicKeyExt.__neg__
PublicKey.__sub__ = PublicKeyExt.__sub__
PublicKey.mult = PublicKeyExt.mult
PublicKey.__eq__ = PublicKeyExt.__eq__
PublicKey.to_data = PublicKeyExt.to_data

View File

@@ -5,10 +5,11 @@ from typing import Union
import click import click
import uvicorn import uvicorn
from ecc.curve import Point, secp256k1
from fastapi import FastAPI from fastapi import FastAPI
from loguru import logger from loguru import logger
from secp256k1 import PublicKey
import core.settings as settings import core.settings as settings
from core.base import MintPayloads, SplitPayload, MeltPayload, CheckPayload from core.base import MintPayloads, SplitPayload, MeltPayload, CheckPayload
from core.settings import MINT_PRIVATE_KEY, MINT_SERVER_HOST, MINT_SERVER_PORT 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 = [] amounts = []
B_s = [] B_s = []
for payload in payloads.blinded_messages: for payload in payloads.blinded_messages:
v = payload.dict() amounts.append(payload.amount)
amounts.append(v["amount"]) B_s.append(PublicKey(bytes.fromhex(payload.B_), raw=True))
x = int(v["B_"]["x"])
y = int(v["B_"]["y"])
B_ = Point(x, y, secp256k1)
B_s.append(B_)
try: try:
promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash) promises = await ledger.mint(B_s, amounts, payment_hash=payment_hash)
return promises return promises

View File

@@ -7,10 +7,8 @@ from core.db import Connection, Database
async def store_promise( async def store_promise(
amount: int, amount: int,
B_x: str, B_: str,
B_y: str, C_: str,
C_x: str,
C_y: str,
db: Database, db: Database,
conn: Optional[Connection] = None, conn: Optional[Connection] = None,
): ):
@@ -18,15 +16,13 @@ async def store_promise(
await (conn or db).execute( await (conn or db).execute(
""" """
INSERT INTO promises INSERT INTO promises
(amount, B_x, B_y, C_x, C_y) (amount, B_b, C_b)
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?)
""", """,
( (
amount, amount,
str(B_x), str(B_),
str(B_y), str(C_),
str(C_x),
str(C_y),
), ),
) )
@@ -54,13 +50,12 @@ async def invalidate_proof(
await (conn or db).execute( await (conn or db).execute(
""" """
INSERT INTO proofs_used INSERT INTO proofs_used
(amount, C_x, C_y, secret) (amount, C, secret)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?)
""", """,
( (
proof.amount, proof.amount,
str(proof.C.x), str(proof.C),
str(proof.C.y),
str(proof.secret), str(proof.secret),
), ),
) )

View File

@@ -3,12 +3,10 @@ Implementation of https://gist.github.com/phyro/935badc682057f418842c72961cf096c
""" """
import hashlib import hashlib
import math from core.secp import PrivateKey, PublicKey
from ecc.curve import Point, secp256k1
from ecc.key import gen_keypair
from typing import List from typing import List, Set
from core.base import Proof, BlindedMessage, BlindedSignature, BasePoint from core.base import Proof, BlindedMessage, BlindedSignature
import core.b_dhke as b_dhke import core.b_dhke as b_dhke
from core.base import Invoice from core.base import Invoice
@@ -30,49 +28,49 @@ from mint.crud import (
class Ledger: class Ledger:
def __init__(self, secret_key: str, db: str): def __init__(self, secret_key: str, db: str):
self.proofs_used = set() self.proofs_used: Set[str] = set()
self.master_key = secret_key self.master_key: str = secret_key
self.keys = self._derive_keys(self.master_key) self.keys: List[PrivateKey] = self._derive_keys(self.master_key)
self.pub_keys = self._derive_pubkeys(self.keys) self.pub_keys: List[PublicKey] = self._derive_pubkeys(self.keys)
self.db = Database("mint", db) self.db: Database = Database("mint", db)
async def load_used_proofs(self): async def load_used_proofs(self):
self.proofs_used = set(await get_proofs_used(db=self.db)) self.proofs_used = set(await get_proofs_used(db=self.db))
@staticmethod @staticmethod
def _derive_keys(master_key): def _derive_keys(master_key: str):
"""Deterministic derivation of keys for 2^n values.""" """Deterministic derivation of keys for 2^n values."""
return { return {
2 2
** i: int( ** i: PrivateKey(
hashlib.sha256((str(master_key) + str(i)).encode("utf-8")) hashlib.sha256((str(master_key) + str(i)).encode("utf-8"))
.hexdigest() .hexdigest()
.encode("utf-8"), .encode("utf-8")[:32],
16, raw=True,
) )
for i in range(MAX_ORDER) for i in range(MAX_ORDER)
} }
@staticmethod @staticmethod
def _derive_pubkeys(keys): def _derive_pubkeys(keys: List[PrivateKey]):
return { return {amt: keys[amt].pubkey for amt in [2**i for i in range(MAX_ORDER)]}
amt: keys[amt] * secp256k1.G for amt in [2**i for i in range(MAX_ORDER)]
}
async def _generate_promises(self, amounts, B_s): async def _generate_promises(self, amounts, B_s):
"""Generates promises that sum to the given amount.""" """Generates promises that sum to the given amount."""
return [ 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) 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').""" """Generates a promise for given amount and returns a pair (amount, C')."""
secret_key = self.keys[amount] # Get the correct key secret_key = self.keys[amount] # Get the correct key
C_ = b_dhke.step2_alice(B_, secret_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) await store_promise(
return BlindedSignature(amount=amount, C_=BasePoint(x=C_.x, y=C_.y)) 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): def _check_spendable(self, proof: Proof):
"""Checks whether the proof was already spent.""" """Checks whether the proof was already spent."""
@@ -83,10 +81,12 @@ class Ledger:
if not self._check_spendable(proof): if not self._check_spendable(proof):
raise Exception(f"tokens 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 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) 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""" """Verifies the expected split was correctly computed"""
fst_amt, snd_amt = total - amount, amount # we have two amounts to split to fst_amt, snd_amt = total - amount, amount # we have two amounts to split to
fst_outputs = amount_split(fst_amt) fst_outputs = amount_split(fst_amt)
@@ -95,16 +95,18 @@ class Ledger:
given = [o.amount for o in output_data] given = [o.amount for o in output_data]
return given == expected 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] secrets = [p.secret for p in proofs]
if len(secrets) != len(list(set(secrets))): if len(secrets) != len(list(set(secrets))):
return False return False
B_xs = [od.B_.x for od in output_data] B_s = [od.B_ for od in output_data]
if len(B_xs) != len(list(set(B_xs))): if len(B_s) != len(list(set(B_s))):
return False return False
return True 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.""" """Split amount like output amount can't be negative or too big."""
try: try:
self._verify_amount(amount) self._verify_amount(amount)
@@ -179,7 +181,7 @@ class Ledger:
# Public methods # Public methods
def get_pubkeys(self): def get_pubkeys(self):
"""Returns public keys for possible amounts.""" """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): async def request_mint(self, amount):
"""Returns Lightning invoice and stores it in the db.""" """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]): if not all([self._verify_proof(p) for p in proofs]):
return False 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): if not self._verify_no_duplicates(proofs, output_data):
raise Exception("duplicate proofs or promises") raise Exception("duplicate proofs or promises")

View File

@@ -17,12 +17,10 @@ async def m001_initial(db: Database):
""" """
CREATE TABLE IF NOT EXISTS promises ( CREATE TABLE IF NOT EXISTS promises (
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
B_x TEXT NOT NULL, B_b TEXT NOT NULL,
B_y TEXT NOT NULL, C_b TEXT NOT NULL,
C_x TEXT NOT NULL,
C_y 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 ( CREATE TABLE IF NOT EXISTS proofs_used (
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
C_x TEXT NOT NULL, C TEXT NOT NULL,
C_y TEXT NOT NULL,
secret TEXT NOT NULL, secret TEXT NOT NULL,
UNIQUE (secret) UNIQUE (secret)

44
poetry.lock generated
View File

@@ -127,40 +127,6 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "dataclasses"
version = "0.6"
description = "A backport of the dataclasses module for Python 3.6"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "ecc"
version = "0.0.1"
description = "Pure Python implementation of an elliptic curve cryptosystem based on FIPS 186-3"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "ecc-pycrypto"
version = "1.0.0"
description = ""
category = "main"
optional = false
python-versions = ">=3.6"
develop = false
[package.dependencies]
dataclasses = "*"
[package.source]
type = "git"
url = "https://github.com/lc6chang/ecc-pycrypto.git"
reference = "v1.0.1"
resolved_reference = "eb8b8c19a81a52d9cf705d90a597a78cdaf2b6f6"
[[package]] [[package]]
name = "ecdsa" name = "ecdsa"
version = "0.18.0" version = "0.18.0"
@@ -707,7 +673,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "27d48020dabbc74117941ab884e1794d502d38c1380c777ee0edb203ada3c7b1" content-hash = "8046f708fe138fcdb9c1e39e18e4c466292f183f0d1736ecee66ec4854ad54cc"
[metadata.files] [metadata.files]
anyio = [ anyio = [
@@ -838,14 +804,6 @@ colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
] ]
dataclasses = [
{file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"},
{file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"},
]
ecc = [
{file = "ecc-0.0.1.zip", hash = "sha256:4bbcd46e9963ca37422d3244ab503af9dce95cbd35f676f7f9a4dd6306e23538"},
]
ecc-pycrypto = []
ecdsa = [ ecdsa = [
{file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
{file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},

View File

@@ -8,7 +8,6 @@ license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.8"
pycrypto = "^2.6.1" pycrypto = "^2.6.1"
ecc = "0.0.1"
requests = "2.27.1" requests = "2.27.1"
pytest-asyncio = "0.19.0" pytest-asyncio = "0.19.0"
SQLAlchemy = "1.3.24" SQLAlchemy = "1.3.24"
@@ -22,7 +21,6 @@ Jinja2 = "3.0.3"
MarkupSafe = "2.1.1" MarkupSafe = "2.1.1"
urllib3 = "1.23" urllib3 = "1.23"
Werkzeug = "2.2.2" Werkzeug = "2.2.2"
ecc-pycrypto = {git = "https://github.com/lc6chang/ecc-pycrypto.git", rev = "v1.0.1"}
asgiref = "^3.5.2" asgiref = "^3.5.2"
pydantic = "^1.10.2" pydantic = "^1.10.2"
bech32 = "^1.2.0" bech32 = "^1.2.0"

View File

@@ -13,13 +13,12 @@ async def store_proof(
await (conn or db).execute( await (conn or db).execute(
""" """
INSERT INTO proofs INSERT INTO proofs
(amount, C_x, C_y, secret) (amount, C, secret)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?)
""", """,
( (
proof.amount, proof.amount,
str(proof.C.x), str(proof.C),
str(proof.C.y),
str(proof.secret), str(proof.secret),
), ),
) )
@@ -55,13 +54,12 @@ async def invalidate_proof(
await (conn or db).execute( await (conn or db).execute(
""" """
INSERT INTO proofs_used INSERT INTO proofs_used
(amount, C_x, C_y, secret) (amount, C, secret)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?)
""", """,
( (
proof.amount, proof.amount,
str(proof.C.x), str(proof.C),
str(proof.C.y),
str(proof.secret), str(proof.secret),
), ),
) )

View File

@@ -17,8 +17,7 @@ async def m001_initial(db: Database):
""" """
CREATE TABLE IF NOT EXISTS proofs ( CREATE TABLE IF NOT EXISTS proofs (
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
C_x TEXT NOT NULL, C TEXT NOT NULL,
C_y TEXT NOT NULL,
secret TEXT NOT NULL, secret TEXT NOT NULL,
UNIQUE (secret) UNIQUE (secret)
@@ -31,8 +30,7 @@ async def m001_initial(db: Database):
""" """
CREATE TABLE IF NOT EXISTS proofs_used ( CREATE TABLE IF NOT EXISTS proofs_used (
amount INTEGER NOT NULL, amount INTEGER NOT NULL,
C_x TEXT NOT NULL, C TEXT NOT NULL,
C_y TEXT NOT NULL,
secret TEXT NOT NULL, secret TEXT NOT NULL,
UNIQUE (secret) UNIQUE (secret)

View File

@@ -2,11 +2,10 @@ import random
from typing import List from typing import List
import requests import requests
from ecc.curve import Point, secp256k1 from core.secp import PublicKey
import core.b_dhke as b_dhke import core.b_dhke as b_dhke
from core.base import ( from core.base import (
BasePoint,
BlindedMessage, BlindedMessage,
MintPayloads, MintPayloads,
Proof, Proof,
@@ -29,7 +28,8 @@ class LedgerAPI:
def _get_keys(url): def _get_keys(url):
resp = requests.get(url + "/keys").json() resp = requests.get(url + "/keys").json()
return { return {
int(amt): Point(val["x"], val["y"], secp256k1) for amt, val in resp.items() int(amt): PublicKey(bytes.fromhex(val), raw=True)
for amt, val in resp.items()
} }
@staticmethod @staticmethod
@@ -46,10 +46,9 @@ class LedgerAPI:
"""Returns proofs of promise from promises.""" """Returns proofs of promise from promises."""
proofs = [] proofs = []
for promise, (r, secret) in zip(promises, secrets): for promise, (r, secret) in zip(promises, secrets):
C_ = Point(promise.C_.x, promise.C_.y, secp256k1) C_ = PublicKey(bytes.fromhex(promise.C_), raw=True)
C = b_dhke.step3_bob(C_, r, self.keys[promise.amount]) C = b_dhke.step3_bob(C_, r, self.keys[promise.amount])
c_point = BasePoint(x=C.x, y=C.y) proof = Proof(amount=promise.amount, C=C.serialize().hex(), secret=secret)
proof = Proof(amount=promise.amount, C=c_point, secret=secret)
proofs.append(proof) proofs.append(proof)
return proofs return proofs
@@ -63,13 +62,14 @@ class LedgerAPI:
payloads: MintPayloads = MintPayloads() payloads: MintPayloads = MintPayloads()
secrets = [] secrets = []
rs = [] rs = []
for i, amount in enumerate(amounts): for amount in amounts:
secret = str(random.getrandbits(128)) secret = str(random.getrandbits(128))
secrets.append(secret) secrets.append(secret)
B_, r = b_dhke.step1_bob(secret) B_, r = b_dhke.step1_bob(secret)
rs.append(r) rs.append(r)
blinded_point = BasePoint(x=str(B_.x), y=str(B_.y)) payload: BlindedMessage = BlindedMessage(
payload: BlindedMessage = BlindedMessage(amount=amount, B_=blinded_point) amount=amount, B_=B_.serialize().hex()
)
payloads.blinded_messages.append(payload) payloads.blinded_messages.append(payload)
promises_dict = requests.post( promises_dict = requests.post(
self.url + "/mint", self.url + "/mint",
@@ -94,9 +94,8 @@ class LedgerAPI:
secret = str(random.getrandbits(128)) secret = str(random.getrandbits(128))
B_, r = b_dhke.step1_bob(secret) B_, r = b_dhke.step1_bob(secret)
secrets.append((r, secret)) secrets.append((r, secret))
blinded_point = BasePoint(x=str(B_.x), y=str(B_.y))
payload: BlindedMessage = BlindedMessage( payload: BlindedMessage = BlindedMessage(
amount=output_amt, B_=blinded_point amount=output_amt, B_=B_.serialize().hex()
) )
payloads.blinded_messages.append(payload) payloads.blinded_messages.append(payload)
split_payload = SplitPayload(proofs=proofs, amount=amount, output_data=payloads) split_payload = SplitPayload(proofs=proofs, amount=amount, output_data=payloads)