Skip to content

Commit

Permalink
stamp CLI command and db
Browse files Browse the repository at this point in the history
  • Loading branch information
callebtc committed Aug 4, 2023
1 parent c71b513 commit 74b1532
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 12 deletions.
24 changes: 24 additions & 0 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,30 @@ def to_dict(self, include_stamps=False):
d["stamp"] = self.stamp.dict()
return d

@classmethod
def from_row(cls, rowObj: Row):
row = dict(rowObj)
return cls(
id=row["id"],
amount=row["amount"],
secret=row["secret"],
C=row["C"],
stamp=StampSignature(e=row["stamp_e"], s=row["stamp_s"])
if row["stamp_e"] and row["stamp_s"]
else None,
# p2pksigs=json.loads(row["p2pksigs"]) if row["p2pksigs"] else None,
# p2shscript=P2SHScript(
# script=row["script"], signature=row["signature"]
# ) # type: ignore
# if row["script"]
# else None,
reserved=row["reserved"],
send_id=row["send_id"],
time_created=row["time_created"],
time_reserved=row["time_reserved"],
derivation_path=row["derivation_path"],
)

def __getitem__(self, key):
return self.__getattribute__(key)

Expand Down
6 changes: 4 additions & 2 deletions cashu/core/crypto/b_dhke.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def verify(a: PrivateKey, C: PublicKey, secret_msg: str) -> bool:

# stamps
"""
Proves that a in A = a*G is the same as a in C_ = a*Y
Proves that a in A = a*G is the same as a in C = a*Y
Bob:
R1 = rG
Expand All @@ -88,8 +88,11 @@ def verify(a: PrivateKey, C: PublicKey, secret_msg: str) -> bool:
Y = hash_to_curve(x)
R1 = sG - eA
R2 = sY - eC
(eaY = eC, since C' - rA = aY + arG - arG = aY = C)
e == hash(R1, R2, Y, C) (verification)
If true, C must have originated from Bob with private key a
"""


Expand Down Expand Up @@ -129,7 +132,6 @@ def stamp_step2_alice_verify(
assert s.pubkey
R1: PublicKey = s.pubkey - A.mult(e) # type: ignore # R1 = sG - eA
R2: PublicKey = Y.mult(s) - C.mult(e) # type: ignore # R2 = sY - eC
print(R1.serialize().hex(), R2.serialize().hex())
e_bytes = e.private_key
return e_bytes == hash_e(R1, R2, Y, C)

Expand Down
36 changes: 36 additions & 0 deletions cashu/wallet/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,3 +761,39 @@ async def restore(ctx: Context, to: int, batch: int):
await wallet.restore_wallet_from_mnemonic(mnemonic, to=to, batch=batch)
await wallet.load_proofs()
wallet.status()


@cli.command("stamp", help="Stamp tokens in wallet.")
@click.option(
"--max-amount",
"-m",
default=None,
help="Maximum amount to stamp.",
type=int,
)
@click.pass_context
@coro
async def stamp(ctx: Context, max_amount: int):
wallet: Wallet = ctx.obj["WALLET"]
await wallet.load_mint()
await wallet.load_proofs()
proofs_without_stamp = [p for p in wallet.proofs if not p.stamp]
if len(proofs_without_stamp) == 0:
print("All tokens in wallet are stamped.")
return

if max_amount:
# sort proofs by amount and remove largest proofs until max_amount is reached
proofs_without_stamp = sorted(
proofs_without_stamp, key=lambda x: x.amount, reverse=True
)
while sum_proofs(proofs_without_stamp) > max_amount:
proofs_without_stamp.pop(0)
if len(proofs_without_stamp) == 0:
print(f"No tokens smaller than {max_amount} sat in wallet.")
return

print(
f"Requesting {len(proofs_without_stamp)} stamps for tokens worth {sum_proofs(proofs_without_stamp)} sat."
)
await wallet.get_proofs_stamps(proofs_without_stamp)
16 changes: 12 additions & 4 deletions cashu/wallet/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def get_proofs(
SELECT * from proofs
"""
)
return [Proof(**dict(r)) for r in rows]
return [Proof.from_row(r) for r in rows]


async def get_reserved_proofs(
Expand Down Expand Up @@ -82,10 +82,13 @@ async def invalidate_proof(
)


async def update_proof_reserved(
async def update_proof(
proof: Proof,
reserved: bool,
send_id: str = "",
*,
reserved: Optional[bool] = None,
send_id: Optional[str] = None,
stamp_e: Optional[str] = None,
stamp_s: Optional[str] = None,
db: Optional[Database] = None,
conn: Optional[Connection] = None,
):
Expand All @@ -103,6 +106,11 @@ async def update_proof_reserved(
clauses.append("time_reserved = ?")
values.append(int(time.time()))

if stamp_e and stamp_s:
clauses.append("stamp_e = ?")
values.append(stamp_e)
clauses.append("stamp_s = ?")
values.append(stamp_s)
await (conn or db).execute( # type: ignore
f"UPDATE proofs SET {', '.join(clauses)} WHERE secret = ?",
(*values, str(proof.secret)),
Expand Down
4 changes: 2 additions & 2 deletions cashu/wallet/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,6 @@ async def m009_privatekey_and_determinstic_key_derivation(db: Database):

async def m010_proofs_add_stamps(db: Database):
await db.execute("ALTER TABLE proofs ADD COLUMN stamp_e TEXT")
await db.execute("ALTER TABLE proofs ADD COLUMN stamp_r TEXT")
await db.execute("ALTER TABLE proofs ADD COLUMN stamp_s TEXT")
await db.execute("ALTER TABLE proofs_used ADD COLUMN stamp_e TEXT")
await db.execute("ALTER TABLE proofs_used ADD COLUMN stamp_r TEXT")
await db.execute("ALTER TABLE proofs_used ADD COLUMN stamp_s TEXT")
11 changes: 7 additions & 4 deletions cashu/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
store_proof,
store_seed_and_mnemonic,
update_lightning_invoice,
update_proof_reserved,
update_proof,
)
from . import migrations

Expand Down Expand Up @@ -1244,6 +1244,7 @@ async def get_proofs_stamps(self, proofs: List[Proof]):
"""

stamp_response = await super().get_proofs_stamps(proofs)
logger.trace(stamp_response)
stamps = stamp_response.stamps
for proof, stamp in zip(proofs, stamps):
assert b_dhke.stamp_step2_alice_verify(
Expand All @@ -1253,6 +1254,10 @@ async def get_proofs_stamps(self, proofs: List[Proof]):
e=PrivateKey(bytes.fromhex(stamp.e), raw=True),
A=self.public_keys[proof.amount],
), "stamp verification failed."

await update_proof(
proof=proof, stamp_e=stamp.e, stamp_s=stamp.s, db=self.db
)
return True

# ---------- TOKEN MECHANIS ----------
Expand Down Expand Up @@ -1472,9 +1477,7 @@ async def set_reserved(self, proofs: List[Proof], reserved: bool) -> None:
uuid_str = str(uuid.uuid1())
for proof in proofs:
proof.reserved = True
await update_proof_reserved(
proof, reserved=reserved, send_id=uuid_str, db=self.db
)
await update_proof(proof, reserved=reserved, send_id=uuid_str, db=self.db)

async def invalidate(
self, proofs: List[Proof], check_spendable=True
Expand Down

0 comments on commit 74b1532

Please sign in to comment.