Skip to content

Commit

Permalink
format
Browse files Browse the repository at this point in the history
  • Loading branch information
callebtc committed Jul 30, 2023
1 parent 4960635 commit 068224e
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 100 deletions.
17 changes: 16 additions & 1 deletion cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ class DLEQ(BaseModel):
s: str


class DLEQWallet(BaseModel):
"""
Discrete Log Equality (DLEQ) Proof
"""

e: str
s: str
r: str # blinding_factor, unknown to mint but sent from wallet to wallet for DLEQ proof
# B_: Union[str, None] = None # blinded message, sent to the mint by the wallet
# C_: Union[str, None] = None # blinded signature, received by the mint


class Proof(BaseModel):
"""
Value token
Expand All @@ -171,10 +183,12 @@ class Proof(BaseModel):
id: Union[
None, str
] = "" # NOTE: None for backwards compatibility for old clients that do not include the keyset id < 0.3

amount: int = 0
secret: str = "" # secret or message to be blinded and signed
C: str = "" # signature on secret, unblinded by wallet
dleq: Union[DLEQ, None] = None # DLEQ proof
dleq: Union[DLEQWallet, None] = None # DLEQ proof

p2pksigs: Union[List[str], None] = [] # P2PK signature
p2shscript: Union[P2SHScript, None] = None # P2SH spending condition
reserved: Union[
Expand All @@ -193,6 +207,7 @@ def to_dict(self, include_dleq=False):
return dict(id=self.id, amount=self.amount, secret=self.secret, C=self.C)

assert self.dleq, "DLEQ proof is missing"
print(self.dleq)
return dict(
id=self.id,
amount=self.amount,
Expand Down
57 changes: 39 additions & 18 deletions cashu/core/crypto/b_dhke.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def step1_alice(
return B_, r


def step2_bob(B_: PublicKey, a: PrivateKey) -> Tuple[PublicKey, bytes, bytes]:
def step2_bob(B_: PublicKey, a: PrivateKey) -> Tuple[PublicKey, PrivateKey, PrivateKey]:
C_: PublicKey = B_.mult(a) # type: ignore
# produce dleq proof
e, s = step2_bob_dleq(B_, a)
Expand All @@ -97,7 +97,7 @@ def verify(a: PrivateKey, C: PublicKey, secret_msg: str) -> bool:
return C == Y.mult(a) # type: ignore


def hash_e(R1: PublicKey, R2: PublicKey, K: PublicKey, C_: PublicKey):
def hash_e(R1: PublicKey, R2: PublicKey, K: PublicKey, C_: PublicKey) -> bytes:
_R1 = R1.serialize(compressed=False).hex()
_R2 = R2.serialize(compressed=False).hex()
_K = K.serialize(compressed=False).hex()
Expand All @@ -107,7 +107,9 @@ def hash_e(R1: PublicKey, R2: PublicKey, K: PublicKey, C_: PublicKey):
return e


def step2_bob_dleq(B_: PublicKey, a: PrivateKey, p_bytes: bytes = b""):
def step2_bob_dleq(
B_: PublicKey, a: PrivateKey, p_bytes: bytes = b""
) -> Tuple[PrivateKey, PrivateKey]:
if p_bytes:
# deterministic p for testing
p = PrivateKey(privkey=p_bytes, raw=True)
Expand All @@ -117,23 +119,42 @@ def step2_bob_dleq(B_: PublicKey, a: PrivateKey, p_bytes: bytes = b""):

R1 = p.pubkey # R1 = pG
assert R1
R2 = B_.mult(p) # R2 = pB_ # type: ignore
C_ = B_.mult(a) # C_ = aB_ # type: ignore
K = a.pubkey
assert K
e = hash_e(R1, R2, K, C_) # e = hash(R1, R2, K, C_)
R2: PublicKey = B_.mult(p) # R2 = pB_ # type: ignore
C_: PublicKey = B_.mult(a) # C_ = aB_ # type: ignore
A = a.pubkey
assert A
e = hash_e(R1, R2, A, C_) # e = hash(R1, R2, A, C_)
s = p.tweak_add(a.tweak_mul(e)) # s = p + ek
return e, s


def alice_verify_dleq(e: bytes, s: bytes, K: PublicKey, B_: bytes, C_: bytes):
epk = PrivateKey(e, raw=True)
spk = PrivateKey(s, raw=True)
bk = PublicKey(B_, raw=True)
ck = PublicKey(C_, raw=True)
R1 = spk.pubkey - K.mult(epk) # type: ignore
R2 = bk.mult(spk) - ck.mult(epk) # type: ignore
return e == hash_e(R1, R2, K, ck)
epk = PrivateKey(e, raw=True)
return epk, spk


def alice_verify_dleq(
e: PrivateKey, s: PrivateKey, A: PublicKey, B_: PublicKey, C_: PublicKey
):
R1 = s.pubkey - A.mult(e) # type: ignore
R2 = B_.mult(s) - C_.mult(e) # type: ignore
e_bytes = e.private_key
return e_bytes == hash_e(R1, R2, A, C_)


def carol_verify_dleq(
secret_msg: str,
r: PrivateKey,
C: PublicKey,
e: PrivateKey,
s: PrivateKey,
A: PublicKey,
):
Y: PublicKey = hash_to_curve(secret_msg.encode("utf-8"))
C_: PublicKey = C + A.mult(r) # type: ignore
B_: PublicKey = Y + r.pubkey # type: ignore
return alice_verify_dleq(e, s, A, B_, C_)
# R1 = s.pubkey - A.mult(e) # type: ignore
# R2 = B_.mult(s) - C_.mult(e) # type: ignore
# e_bytes = e.private_key
# return e_bytes == hash_e(R1, R2, A, C_)


# Below is a test of a simple positive and negative case
Expand Down
8 changes: 3 additions & 5 deletions cashu/mint/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ async def _generate_promise(
amount=amount,
B_=B_.serialize().hex(),
C_=C_.serialize().hex(),
e=e.hex(),
s=s.hex(),
e=e.serialize(),
s=s.serialize(),
db=self.db,
id=keyset.id,
)
Expand All @@ -201,9 +201,7 @@ async def _generate_promise(
id=keyset.id,
amount=amount,
C_=C_.serialize().hex(),
dleq=DLEQ(
e=e.hex(), s=s.hex()
),
dleq=DLEQ(e=e.serialize(), s=s.serialize()),
)

def _check_spendable(self, proof: Proof):
Expand Down
33 changes: 24 additions & 9 deletions cashu/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
CheckFeesRequest,
CheckSpendableRequest,
CheckSpendableResponse,
DLEQWallet,
GetInfoResponse,
GetMeltResponse,
GetMintResponse,
Expand Down Expand Up @@ -148,12 +149,21 @@ def verify_proofs_dleq(self, proofs: List[Proof]):
return
logger.debug("Verifying DLEQ proof.")
assert self.keys.public_keys
if not b_dhke.alice_verify_dleq(
bytes.fromhex(proof.dleq.e),
bytes.fromhex(proof.dleq.s),
self.keys.public_keys[proof.amount],
bytes.fromhex(proof.dleq.B_),
bytes.fromhex(proof.dleq.C_),
# if not b_dhke.alice_verify_dleq(
# e=PrivateKey(bytes.fromhex(proof.dleq.e), raw=True),
# s=PrivateKey(bytes.fromhex(proof.dleq.s), raw=True),
# A=self.keys.public_keys[proof.amount],
# B_=PublicKey(bytes.fromhex(proof.B_), raw=True),
# C_=PublicKey(bytes.fromhex(proof.C_), raw=True),
# ):
# raise Exception("Alice: DLEQ proof invalid.")
if not b_dhke.carol_verify_dleq(
secret_msg=proof.secret,
C=PublicKey(bytes.fromhex(proof.C), raw=True),
r=PrivateKey(bytes.fromhex(proof.dleq.r), raw=True),
e=PrivateKey(bytes.fromhex(proof.dleq.e), raw=True),
s=PrivateKey(bytes.fromhex(proof.dleq.s), raw=True),
A=self.keys.public_keys[proof.amount],
):
raise Exception("DLEQ proof invalid.")

Expand Down Expand Up @@ -188,17 +198,22 @@ def _construct_proofs(

C_ = PublicKey(bytes.fromhex(promise.C_), raw=True)
C = b_dhke.step3_alice(C_, r, self.public_keys[promise.amount])
B_, r = b_dhke.step1_alice(secret, r) # recompute B_ for dleq proofs

proof = Proof(
id=promise.id,
amount=promise.amount,
C=C.serialize().hex(),
secret=secret,
dleq=promise.dleq,
derivation_path=path,
)
if proof.dleq:
proof.dleq.C_ = promise.C_

# if the mint returned a dleq proof, we add it to the proof
if promise.dleq:
proof.dleq = DLEQWallet(
e=promise.dleq.e, s=promise.dleq.s, r=r.serialize()
)

proofs.append(proof)

logger.trace(
Expand Down
19 changes: 2 additions & 17 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ async def init_wallet():
return wallet



def test_info(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -38,7 +37,6 @@ def test_info(cli_prefix):
assert result.exit_code == 0



def test_info_with_mint(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -52,7 +50,6 @@ def test_info_with_mint(cli_prefix):
assert result.exit_code == 0



def test_info_with_mnemonic(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -66,7 +63,6 @@ def test_info_with_mnemonic(cli_prefix):
assert result.exit_code == 0



def test_balance(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -81,7 +77,6 @@ def test_balance(cli_prefix):
assert result.exit_code == 0



def test_invoice(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -97,7 +92,6 @@ def test_invoice(mint, cli_prefix):
assert result.exit_code == 0



def test_invoice_with_split(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -109,7 +103,6 @@ def test_invoice_with_split(mint, cli_prefix):
# assert wallet.proof_amounts.count(1) >= 10



def test_wallets(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -124,7 +117,6 @@ def test_wallets(cli_prefix):
assert result.exit_code == 0



def test_send(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -139,7 +131,6 @@ def test_send(mint, cli_prefix):
assert token.token[0].proofs[0].dleq is None, "dleq included"



def test_send_with_dleq(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -154,7 +145,6 @@ def test_send_with_dleq(mint, cli_prefix):
assert token.token[0].proofs[0].dleq is not None, "no dleq included"



def test_send_legacy(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -168,7 +158,6 @@ def test_send_legacy(mint, cli_prefix):
assert token_str.startswith("eyJwcm9v"), "output is not as expected"



def test_send_without_split(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -181,7 +170,6 @@ def test_send_without_split(mint, cli_prefix):
assert "cashuA" in result.output, "output does not have a token"



def test_send_without_split_but_wrong_amount(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -191,7 +179,6 @@ def test_send_without_split_but_wrong_amount(mint, cli_prefix):
assert "No proof with this amount found" in str(result.exception)



def test_receive_tokenv3(mint, cli_prefix):
runner = CliRunner()
token = (
Expand All @@ -213,7 +200,6 @@ def test_receive_tokenv3(mint, cli_prefix):
print(result.output)



def test_receive_tokenv3_no_mint(mint, cli_prefix):
# this test works only if the previous test succeeds because we simulate the case where the mint URL is not in the token
# therefore, we need to know the mint keyset already and have the mint URL in the db
Expand All @@ -237,7 +223,6 @@ def test_receive_tokenv3_no_mint(mint, cli_prefix):
print(result.output)



def test_receive_tokenv2(mint, cli_prefix):
runner = CliRunner()
token = (
Expand All @@ -255,7 +240,6 @@ def test_receive_tokenv2(mint, cli_prefix):
print(result.output)



def test_receive_tokenv1(mint, cli_prefix):
runner = CliRunner()
token = (
Expand All @@ -273,6 +257,8 @@ def test_receive_tokenv1(mint, cli_prefix):


()


def test_nostr_send(mint, cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand All @@ -291,7 +277,6 @@ def test_nostr_send(mint, cli_prefix):
print(result.output)



def test_pending(cli_prefix):
runner = CliRunner()
result = runner.invoke(
Expand Down
Loading

0 comments on commit 068224e

Please sign in to comment.