Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mint: add websockets for quote updates #413

Merged
merged 49 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
18b9db4
add websockets for quote updates
callebtc Feb 10, 2024
3a64e21
add test (not working)
callebtc Feb 11, 2024
b549ed8
Merge branch 'main' into add_websocket_quote_updates
callebtc Apr 3, 2024
345aa8a
wip: emit events to everyone
callebtc Apr 3, 2024
7f05035
wip: emit events to everyone
callebtc Apr 3, 2024
8d714b2
wip, lots of things broken but invoice callback works
callebtc Apr 4, 2024
5aea9a4
wip
callebtc Apr 5, 2024
0022287
add wip files
callebtc Apr 5, 2024
41a85df
tests almost passing
callebtc Apr 5, 2024
f5a7075
add task
callebtc Apr 10, 2024
33cd23b
Merge branch 'main' into add_websocket_quote_updates
callebtc Apr 10, 2024
e07ce88
refactor nut constants
callebtc Apr 14, 2024
f4ffb8d
startup fix
callebtc Apr 14, 2024
cf52224
works with old mints
callebtc Apr 14, 2024
5a5eaae
wip cli
callebtc Apr 14, 2024
173aea4
Merge branch 'main' into add_websocket_quote_updates
callebtc Apr 14, 2024
c2dba4d
fix mypy
callebtc Apr 14, 2024
1853e90
remove automatic invoice test now with websockets
callebtc Apr 14, 2024
b1f05c1
remove comment
callebtc Apr 15, 2024
fd58cc2
better logging
callebtc Apr 16, 2024
0aba3d3
send back response
callebtc Apr 16, 2024
8884dc8
add rate limiter to websocket
callebtc Apr 16, 2024
11ef3e5
add rate limiter to subscriptions
callebtc Apr 16, 2024
bf71c7e
refactor websocket ratelimit
callebtc Apr 16, 2024
1368de5
websocket tests
callebtc Apr 17, 2024
3a6b687
subscription kinds
callebtc Apr 17, 2024
6c14452
doesnt start
callebtc Apr 17, 2024
23ec5cf
remove circular import
callebtc Apr 18, 2024
be89ab3
Merge branch 'main' into add_websocket_quote_updates
callebtc Jun 16, 2024
ed48fe6
update
callebtc Jun 16, 2024
30e55c0
fix mypy
callebtc Jun 16, 2024
2ee2034
move test file in test because it fails if it runs later... dunno why
callebtc Jun 16, 2024
d218d7c
adjust websocket NUT-06 settings
callebtc Jun 17, 2024
7cece91
local import and small fix
callebtc Jun 17, 2024
d3ebf79
disable websockets in CLI if "no_check" is selected
callebtc Jun 17, 2024
03c7e36
move subscription test to where it was
callebtc Jun 17, 2024
5c03c3d
check proof state with callback, add tests
callebtc Jun 18, 2024
0ac7c2c
tests: run mint fixture per module instead of per session
callebtc Jun 18, 2024
4f157a3
subscription command name fix
callebtc Jun 19, 2024
97072ae
test per session again
callebtc Jun 19, 2024
9c3f9f7
update test race conditions
callebtc Jun 19, 2024
ffe699f
fix tests
callebtc Jun 19, 2024
e2ac76d
clean up
callebtc Jun 19, 2024
917d46e
tmp
callebtc Jun 20, 2024
bbf08b6
fix db issues and remove cached secrets
callebtc Jun 25, 2024
083d427
fix tests
callebtc Jun 25, 2024
20fa96a
blindly try pipeline
callebtc Jun 25, 2024
50c85da
remove comments
callebtc Jun 25, 2024
65dd5af
comments
callebtc Jun 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ jobs:
os: [ubuntu-latest]
python-version: ["3.10"]
poetry-version: ["1.7.1"]
mint-cache-secrets: ["false", "true"]
mint-only-deprecated: ["false", "true"]
mint-database: ["./test_data/test_mint", "postgres://cashu:cashu@localhost:5432/cashu"]
backend-wallet-class: ["FakeWallet"]
Expand All @@ -24,7 +23,6 @@ jobs:
os: ${{ matrix.os }}
python-version: ${{ matrix.python-version }}
poetry-version: ${{ matrix.poetry-version }}
mint-cache-secrets: ${{ matrix.mint-cache-secrets }}
mint-only-deprecated: ${{ matrix.mint-only-deprecated }}
mint-database: ${{ matrix.mint-database }}
regtest:
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@ on:
os:
default: "ubuntu-latest"
type: string
mint-cache-secrets:
default: "false"
type: string
mint-only-deprecated:
default: "false"
type: string

jobs:
poetry:
name: Run (mint-cache-secrets ${{ inputs.mint-cache-secrets }}, mint-only-deprecated ${{ inputs.mint-only-deprecated }}, mint-database ${{ inputs.mint-database }})
name: Run (db ${{ inputs.mint-database }}, deprecated api ${{ inputs.mint-only-deprecated }})
runs-on: ${{ inputs.os }}
services:
postgres:
Expand Down Expand Up @@ -54,7 +51,6 @@ jobs:
MINT_HOST: localhost
MINT_PORT: 3337
MINT_TEST_DATABASE: ${{ inputs.mint-database }}
MINT_CACHE_SECRETS: ${{ inputs.mint-cache-secrets }}
DEBUG_MINT_ONLY_DEPRECATED: ${{ inputs.mint-only-deprecated }}
TOR: false
run: |
Expand Down
45 changes: 41 additions & 4 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
from typing import Dict, List, Optional, Union

from loguru import logger
from pydantic import BaseModel
from pydantic import BaseModel, root_validator

from cashu.core.json_rpc.base import JSONRPCSubscriptionKinds

from ..mint.events.event_model import LedgerEvent
from .crypto.aes import AESCipher
from .crypto.b_dhke import hash_to_curve
from .crypto.keys import (
Expand Down Expand Up @@ -54,11 +57,27 @@ def __str__(self):
return self.name


class ProofState(BaseModel):
class ProofState(LedgerEvent):
Y: str
state: SpentState
witness: Optional[str] = None

@root_validator()
def check_witness(cls, values):
state, witness = values.get("state"), values.get("witness")
if witness is not None and state != SpentState.spent:
raise ValueError('Witness can only be set if the spent state is "SPENT"')
return values

@property
def identifier(self) -> str:
"""Implementation of the abstract method from LedgerEventManager"""
return self.Y

@property
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.PROOF_STATE


class HTLCWitness(BaseModel):
preimage: Optional[str] = None
Expand Down Expand Up @@ -249,7 +268,7 @@ class Invoice(BaseModel):
time_paid: Union[None, str, int, float] = ""


class MeltQuote(BaseModel):
class MeltQuote(LedgerEvent):
quote: str
method: str
request: str
Expand Down Expand Up @@ -290,8 +309,17 @@ def from_row(cls, row: Row):
proof=row["proof"],
)

@property
def identifier(self) -> str:
"""Implementation of the abstract method from LedgerEventManager"""
return self.quote

@property
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.BOLT11_MELT_QUOTE


class MintQuote(BaseModel):
class MintQuote(LedgerEvent):
quote: str
method: str
request: str
Expand Down Expand Up @@ -329,6 +357,15 @@ def from_row(cls, row: Row):
paid_time=paid_time,
)

@property
def identifier(self) -> str:
"""Implementation of the abstract method from LedgerEventManager"""
return self.quote

@property
def kind(self) -> JSONRPCSubscriptionKinds:
return JSONRPCSubscriptionKinds.BOLT11_MINT_QUOTE


# ------- KEYSETS -------

Expand Down
86 changes: 86 additions & 0 deletions cashu/core/json_rpc/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from enum import Enum
from typing import List

from pydantic import BaseModel, Field

from ..settings import settings


class JSONRPCRequest(BaseModel):
jsonrpc: str = "2.0"
id: int
method: str
params: dict


class JSONRPCResponse(BaseModel):
jsonrpc: str = "2.0"
result: dict
id: int


class JSONRPCNotification(BaseModel):
jsonrpc: str = "2.0"
method: str
params: dict


class JSONRPCErrorCode(Enum):
PARSE_ERROR = -32700
INVALID_REQUEST = -32600
METHOD_NOT_FOUND = -32601
INVALID_PARAMS = -32602
INTERNAL_ERROR = -32603
SERVER_ERROR = -32000
APPLICATION_ERROR = -32099
SYSTEM_ERROR = -32098
TRANSPORT_ERROR = -32097


class JSONRPCError(BaseModel):
code: JSONRPCErrorCode
message: str


class JSONRPCErrorResponse(BaseModel):
jsonrpc: str = "2.0"
error: JSONRPCError
id: int


# Cashu Websocket protocol


class JSONRPCMethods(Enum):
SUBSCRIBE = "subscribe"
UNSUBSCRIBE = "unsubscribe"


class JSONRPCSubscriptionKinds(Enum):
BOLT11_MINT_QUOTE = "bolt11_mint_quote"
BOLT11_MELT_QUOTE = "bolt11_melt_quote"
PROOF_STATE = "proof_state"


class JSONRPCStatus(Enum):
OK = "OK"


class JSONRPCSubscribeParams(BaseModel):
kind: JSONRPCSubscriptionKinds
filters: List[str] = Field(..., max_length=settings.mint_max_request_length)
subId: str


class JSONRPCUnsubscribeParams(BaseModel):
subId: str


class JSONRPCNotficationParams(BaseModel):
subId: str
payload: dict


class JSONRRPCSubscribeResponse(BaseModel):
status: JSONRPCStatus
subId: str
3 changes: 3 additions & 0 deletions cashu/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class GetInfoResponse(BaseModel):
motd: Optional[str] = None
nuts: Optional[Dict[int, Any]] = None

def supports(self, nut: int) -> Optional[bool]:
return nut in self.nuts if self.nuts else None


class Nut15MppSupport(BaseModel):
method: str
Expand Down
13 changes: 13 additions & 0 deletions cashu/core/nuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SWAP_NUT = 3
MINT_NUT = 4
MELT_NUT = 5
INFO_NUT = 6
STATE_NUT = 7
FEE_RETURN_NUT = 8
RESTORE_NUT = 9
SCRIPT_NUT = 10
P2PK_NUT = 11
DLEQ_NUT = 12
DETERMINSTIC_SECRETS_NUT = 13
MPP_NUT = 15
WEBSOCKETS_NUT = 17
10 changes: 8 additions & 2 deletions cashu/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,20 @@ class MintLimits(MintSettings):
title="Maximum mint balance",
description="Maximum mint balance.",
)
mint_websocket_read_timeout: int = Field(
default=10 * 60,
gt=0,
title="Websocket read timeout",
description="Timeout for reading from a websocket.",
)


class FakeWalletSettings(MintSettings):
fakewallet_brr: bool = Field(default=True)
fakewallet_delay_payment: bool = Field(default=False)
fakewallet_delay_outgoing_payment: Optional[int] = Field(default=3)
fakewallet_delay_incoming_payment: Optional[int] = Field(default=3)
fakewallet_stochastic_invoice: bool = Field(default=False)
fakewallet_payment_state: Optional[bool] = Field(default=None)
mint_cache_secrets: bool = Field(default=True)


class MintInformation(CashuSettings):
Expand Down
9 changes: 5 additions & 4 deletions cashu/lightning/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import Coroutine, Optional, Union
from typing import AsyncGenerator, Coroutine, Optional, Union

from pydantic import BaseModel

Expand Down Expand Up @@ -68,6 +68,7 @@ def __str__(self) -> str:

class LightningBackend(ABC):
supports_mpp: bool = False
supports_incoming_payment_stream: bool = False
supported_units: set[Unit]
unit: Unit

Expand Down Expand Up @@ -124,9 +125,9 @@ async def get_payment_quote(
# ) -> InvoiceQuoteResponse:
# pass

# @abstractmethod
# def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
# pass
@abstractmethod
def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
pass


class Unsupported(Exception):
Expand Down
12 changes: 3 additions & 9 deletions cashu/lightning/blink.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# type: ignore
import asyncio
import json
import math
from typing import Dict, Optional, Union
from typing import AsyncGenerator, Dict, Optional, Union

import bolt11
import httpx
Expand Down Expand Up @@ -454,10 +453,5 @@ async def get_payment_quote(
amount=amount.to(self.unit, round="up"),
)


async def main():
pass


if __name__ == "__main__":
asyncio.run(main())
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
raise NotImplementedError("paid_invoices_stream not implemented")
1 change: 1 addition & 0 deletions cashu/lightning/corelightningrest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
class CoreLightningRestWallet(LightningBackend):
supported_units = set([Unit.sat, Unit.msat])
unit = Unit.sat
supports_incoming_payment_stream: bool = True

def __init__(self, unit: Unit = Unit.sat, **kwargs):
self.assert_unit_supported(unit)
Expand Down
Loading
Loading