check pending proofs

This commit is contained in:
callebtc
2022-12-14 22:50:25 +01:00
parent 99e22c0bae
commit d08b8a00f6
3 changed files with 123 additions and 1 deletions

View File

@@ -29,6 +29,18 @@ class LedgerCrud:
return await invalidate_proof(*args, **kwags)
async def get_proofs_pending(*args, **kwags):
return await get_proofs_pending(*args, **kwags)
async def set_proof_pending(*args, **kwags):
return await set_proof_pending(*args, **kwags)
async def unset_proof_pending(*args, **kwags):
return await unset_proof_pending(*args, **kwags)
async def store_keyset(*args, **kwags):
return await store_keyset(*args, **kwags)
@@ -102,6 +114,55 @@ async def invalidate_proof(
)
async def get_proofs_pending(
db: Database,
conn: Optional[Connection] = None,
):
rows = await (conn or db).fetchall(
f"""
SELECT * from {table_with_schema(db, 'proofs_pending')}
"""
)
return [Proof(**r) for r in rows]
async def set_proof_pending(
db: Database,
proof: Proof,
conn: Optional[Connection] = None,
):
# we add the proof and secret to the used list
await (conn or db).execute(
f"""
INSERT INTO {table_with_schema(db, 'proofs_pending')}
(amount, C, secret)
VALUES (?, ?, ?)
""",
(
proof.amount,
str(proof.C),
str(proof.secret),
),
)
async def unset_proof_pending(
proof: Proof,
db: Database,
conn: Optional[Connection] = None,
):
await (conn or db).execute(
f"""
DELETE FROM {table_with_schema(db, 'proofs_pending')}
WHERE secret = ?
""",
(str(proof["secret"]),),
)
async def store_lightning_invoice(
db: Database,
invoice: Invoice,

View File

@@ -258,7 +258,10 @@ class Ledger:
return ok, preimage
async def _invalidate_proofs(self, proofs: List[Proof]):
"""Adds secrets of proofs to the list of knwon secrets and stores them in the db."""
"""
Adds secrets of proofs to the list of known secrets and stores them in the db.
Removes proofs from pending table.
"""
# Mark proofs as used and prepare new promises
proof_msgs = set([p.secret for p in proofs])
self.proofs_used |= proof_msgs
@@ -266,6 +269,38 @@ class Ledger:
for p in proofs:
await self.crud.invalidate_proof(proof=p, db=self.db)
# delete proofs from pending list
await self._unset_proofs_pending(proofs)
async def _set_proofs_pending(self, proofs: List[Proof]):
"""
If none of the proofs is in the pending table (_validate_proofs_pending), adds proofs to
the list of pending proofs or removes them. Used as a mutex for proofs.
"""
# first we check whether these proofs are pending aready
await self._validate_proofs_pending(proofs)
for p in proofs:
await self.crud.set_proof_pending(proof=p, db=self.db)
async def _unset_proofs_pending(self, proofs: List[Proof]):
"""Deletes proofs from pending table."""
# we try: except: this block in order to avoid that any errors here
# could block the _invalidate_proofs() call that happens afterwards.
try:
for p in proofs:
await self.crud.unset_proof_pending(proof=p, db=self.db)
except Exception as e:
print(e)
pass
async def _validate_proofs_pending(self, proofs: List[Proof]):
"""Checks if any of the provided proofs is in the pending proofs table. Raises exception for at least one match."""
proofs_pending = await self.crud.get_proofs_pending(db=self.db)
for p in proofs:
for pp in proofs_pending:
if p.secret == pp.secret:
raise Exception("proofs are pending.")
# Public methods
def get_keyset(self, keyset_id: str = None):
keyset = self.keysets.keysets[keyset_id] if keyset_id else self.keyset
@@ -308,6 +343,10 @@ class Ledger:
async def melt(self, proofs: List[Proof], invoice: str):
"""Invalidates proofs and pays a Lightning invoice."""
# validate and set proofs as pending
await self._set_proofs_pending(proofs)
# Verify proofs
if not all([self._verify_proof(p) for p in proofs]):
raise Exception("could not verify proofs.")
@@ -354,6 +393,10 @@ class Ledger:
keyset: MintKeyset = None,
):
"""Consumes proofs and prepares new promises based on the amount split."""
# set proofs as pending
await self._set_proofs_pending(proofs)
total = sum_proofs(proofs)
# verify that amount is kosher

View File

@@ -128,3 +128,21 @@ async def m004_keysets_add_version(db: Database):
await db.execute(
f"ALTER TABLE {table_with_schema(db, 'keysets')} ADD COLUMN version TEXT"
)
async def m005_pending_proofs_table(db: Database) -> None:
"""
Store pending proofs.
"""
await db.execute(
f"""
CREATE TABLE IF NOT EXISTS {table_with_schema(db, 'proofs_pending')} (
amount INTEGER NOT NULL,
C TEXT NOT NULL,
secret TEXT NOT NULL,
UNIQUE (secret)
);
"""
)