Skip to content

Commit

Permalink
settlement
Browse files Browse the repository at this point in the history
  • Loading branch information
lollerfirst committed Aug 2, 2024
1 parent cb7eea2 commit c6e06e7
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 11 deletions.
8 changes: 4 additions & 4 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,17 +1250,17 @@ class DlcOutcome(BaseModel):
"""
Describes a DLC outcome
"""
k: Optional[str] # The discrete log revealed by the oracle
k: Optional[str] # The blinded attestation secret
t: Optional[int] # The timeout (claim when time is over)
P: str # The payout structure associated with k
P: str # The payout structure associated with this outcome

class DlcSettlement(BaseModel):
"""
Data used to settle an outcome of a DLC
"""
dlc_root: str
outcome: DlcOutcome
merkle_proof: List[str]
outcome: Optional[DlcOutcome]
merkle_proof: Optional[List[str]]
details: Optional[str]

class DlcPayoutForm(BaseModel):
Expand Down
10 changes: 9 additions & 1 deletion cashu/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,12 @@ class DlcNotFoundError(CashuError):
code = 30002

def __init__(self, **kwargs):
super().__init__(self.detail, self.code)
super().__init__(self.detail, self.code)

class DlcSettlementFail(CashuError):
detail = "settlement verification failed: "
code = 30003

def __init__(self, **kwargs):
super().__init__(self.detail, self.code)
self.detail += kwargs['detail']
47 changes: 41 additions & 6 deletions cashu/mint/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
DlcBadInput,
DlcFundingProof,
DLCWitness,
DiscreetLogContract
DiscreetLogContract,
DlcSettlement,
)
from ..core.crypto import b_dhke
from ..core.crypto.dlc import sign_dlc
Expand Down Expand Up @@ -51,6 +52,8 @@
PostMintQuoteRequest,
PostDlcRegistrationRequest,
PostDlcRegistrationResponse,
PostDlcSettleRequest,
PostDlcSettleResponse,
GetDlcStatusResponse,
)
from ..core.settings import settings
Expand Down Expand Up @@ -1148,7 +1151,7 @@ async def register_dlc(self, request: PostDlcRegistrationRequest) -> PostDlcRegi
active_keyset_for_unit = next(
filter(
lambda k: k.active and k.unit == Unit[registration.unit],
self.keysets.values()
self.keysets.values(),
)
)
funding_privkey = next(iter(active_keyset_for_unit.private_keys.values()))
Expand All @@ -1157,10 +1160,9 @@ async def register_dlc(self, request: PostDlcRegistrationRequest) -> PostDlcRegi
registration.funding_amount,
funding_privkey,
)

funding_proof = DlcFundingProof(
dlc_root=registration.dlc_root,
signature=signature.hex()
signature=signature.hex(),
)
dlc = DiscreetLogContract(
settled=False,
Expand All @@ -1178,7 +1180,7 @@ async def register_dlc(self, request: PostDlcRegistrationRequest) -> PostDlcRegi
dlc_root=registration.dlc_root,
bad_inputs=[DlcBadInput(
index=-1,
detail=e.detail
detail=e.detail,
)]
))
# DLC verification fail
Expand All @@ -1195,4 +1197,37 @@ async def register_dlc(self, request: PostDlcRegistrationRequest) -> PostDlcRegi
return PostDlcRegistrationResponse(
funded=[reg[1] for reg in registered],
errors=errors if len(errors) > 0 else None,
)
)

'''
# UNFINISHED
async def settle_dlc(self, request: PostDlcSettleRequest) -> PostDlcSettleResponse:
"""Settle DLCs once the oracle reveals the attestation secret or the timeout is over.
Args:
request (PostDlcSettleRequest): a request formatted following NUT-DLC spec
Returns:
PostDlcSettleResponse: Indicates which DLCs have been settled and potential errors.
"""
logger.trace("settle called")
verified: List[DlcSettlement] = []
errors: List[DlcSettlement] = []
for settlement in request:
try:
# Verify inclusion of payout structure and associated attestation in the DLC
assert settlement.outcome and settlement.merkle_proof, "outcome or merkle proof not provided"
await self.verify_dlc_inclusion(settlement.dlc_root, settlement.outcome, settlement.merkle_proof)
verified.append(settlement)
except DlcSettlementFail, AssertionError as e:
errors.append(DlcSettlement(
dlc_root=settlement.dlc_root,
details=e.details if isinstance(e, DlcSettlementFail) else str(e)
))
# Database dance:
settled, db_errors = await self.db_write._settle_dlc(verified)
errors += db_errors
return PostDlcSettleResponse(
settled=settled,
errors=errors if len(errors) > 0 else None,
)
'''
25 changes: 25 additions & 0 deletions cashu/mint/verification.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Dict, List, Literal, Optional, Tuple, Union

import json

from loguru import logger
from hashlib import sha256

Expand All @@ -12,6 +14,7 @@
Unit,
DlcBadInput,
DLCWitness,
DlcOutcome,
)
from ..core.crypto import b_dhke
from ..core.crypto.dlc import merkle_verify
Expand All @@ -25,6 +28,7 @@
TransactionError,
TransactionUnitError,
DlcVerificationFail,
DlcSettlementFail,
)
from ..core.settings import settings
from ..lightning.base import LightningBackend
Expand Down Expand Up @@ -475,3 +479,24 @@ def raise_if_err(err):
detail=exc.detail if exc else "input spending conditions verification failed"
))
raise_if_err(errors)

async def _verify_dlc_payout(self, P: str):
try:
payout = json.loads(P)
if not isinstance(payout, dict):
raise DlcSettlementFail(detail="Provided payout structure is not a dictionary")
if not all([isinstance(k, str) and isinstance(v, int) for k, v in payout.items()]):
raise DlcSettlementFail(detail="Provided payout structure is not a dictionary mapping strings to integers")
for v in payout.values():
try:
b = bytes.fromhex(v)
if b[0] != b'\x02':
raise DlcSettlementFail(detail="Provided payout structure contains incorrect public keys")
except ValueError as e:
raise DlcSettlementFail(detail=str(e))
except json.JSONDecodeError as e:
raise DlcSettlementFail(detail="cannot decode the provided payout structure")

async def _verify_dlc_inclusion(self, dlc_root: str, outcome: DlcOutcome, merkle_proof: List[str]):
# Verify payout structure
await self._verify_dlc_payout(outcome.P)

0 comments on commit c6e06e7

Please sign in to comment.