diff --git a/cashu/core/base.py b/cashu/core/base.py index 5cef1499..426a0080 100644 --- a/cashu/core/base.py +++ b/cashu/core/base.py @@ -160,12 +160,24 @@ class GetInfoResponse(BaseModel): # ------- API: KEYS ------- +class KeysResponseKeyset(BaseModel): + id: str + symbol: str + keys: Dict[str, str] + + class KeysResponse(BaseModel): - __root__: Dict[str, str] + keysets: List[KeysResponseKeyset] + + +class KeysetsResponseKeyset(BaseModel): + id: str + symbol: str + active: bool class KeysetsResponse(BaseModel): - keysets: list[str] + keysets: list[KeysetsResponseKeyset] # ------- API: MINT ------- @@ -350,10 +362,12 @@ class MintKeyset: valid_to: Union[str, None] = None first_seen: Union[str, None] = None active: Union[bool, None] = True + symbol: str = "sat" version: Union[str, None] = None def __init__( self, + *, id="", valid_from=None, valid_to=None, @@ -361,6 +375,7 @@ def __init__( active=None, seed: str = "", derivation_path: str = "", + symbol: str = "sat", version: str = "1", ): self.derivation_path = derivation_path @@ -369,11 +384,20 @@ def __init__( self.valid_to = valid_to self.first_seen = first_seen self.active = active + self.symbol = symbol self.version = version # generate keys from seed if seed: self.generate_keys(seed) + @property + def public_keys_hex(self) -> Dict[int, str]: + assert self.public_keys, "public keys not set" + return { + int(amount): key.serialize().hex() + for amount, key in self.public_keys.items() + } + def generate_keys(self, seed): """Generates keys of a keyset from a seed.""" backwards_compatibility_pre_0_12 = False diff --git a/cashu/mint/router.py b/cashu/mint/router.py index 8b00d0c1..171fdaa1 100644 --- a/cashu/mint/router.py +++ b/cashu/mint/router.py @@ -13,7 +13,9 @@ GetMeltResponse, GetMintResponse, KeysetsResponse, + KeysetsResponseKeyset, KeysResponse, + KeysResponseKeyset, PostMeltRequest, PostMintRequest, PostMintResponse, @@ -68,9 +70,13 @@ async def info() -> GetInfoResponse: async def keys(): """This endpoint returns a dictionary of all supported token values of the mint and their associated public key.""" logger.trace("> GET /keys") - keyset = ledger.get_keyset() - keys = KeysResponse.parse_obj(keyset) - return keys.__root__ + keyset = ledger.keyset + keyset_for_response = KeysResponseKeyset( + id=keyset.id, + symbol=keyset.symbol, + keys={str(k): v for k, v in keyset.public_keys_hex.items()}, + ) + return KeysResponse(keysets=[keyset_for_response]) @router.get( @@ -92,8 +98,15 @@ async def keyset_keys(idBase64Urlsafe: str): logger.trace(f"> GET /keys/{idBase64Urlsafe}") id = idBase64Urlsafe.replace("-", "+").replace("_", "/") keyset = ledger.get_keyset(keyset_id=id) - keys = KeysResponse.parse_obj(keyset) - return keys.__root__ + keyset = ledger.keysets.keysets.get(id) + if not keyset: + raise CashuError(code=0, detail="Keyset not found.") + keyset_for_response = KeysResponseKeyset( + id=keyset.id, + symbol=keyset.symbol, + keys={str(k): v for k, v in keyset.public_keys_hex.items()}, + ) + return KeysResponse(keysets=[keyset_for_response]) @router.get( @@ -106,8 +119,12 @@ async def keyset_keys(idBase64Urlsafe: str): async def keysets() -> KeysetsResponse: """This endpoint returns a list of keysets that the mint currently supports and will accept tokens from.""" logger.trace("> GET /keysets") - keysets = KeysetsResponse(keysets=ledger.keysets.get_ids()) - return keysets + keysets = [] + for keyset in ledger.keysets.keysets.values(): + keysets.append( + KeysetsResponseKeyset(id=keyset.id, symbol=keyset.symbol, active=True) + ) + return KeysetsResponse(keysets=keysets) @router.get( diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index c761e507..265ed222 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -25,6 +25,7 @@ GetMintResponse, Invoice, KeysetsResponse, + KeysResponse, PostMeltRequest, PostMintRequest, PostMintResponse, @@ -249,11 +250,12 @@ async def _get_keys(self, url: str) -> WalletKeyset: url + "/keys", ) self.raise_on_error(resp) - keys: dict = resp.json() - assert len(keys), Exception("did not receive any keys") + keys_dict: dict = resp.json() + assert len(keys_dict), Exception("did not receive any keys") + keys = KeysResponse.parse_obj(keys_dict) keyset_keys = { int(amt): PublicKey(bytes.fromhex(val), raw=True) - for amt, val in keys.items() + for amt, val in keys.keysets[0].keys.items() } keyset = WalletKeyset(public_keys=keyset_keys, mint_url=url) return keyset @@ -278,11 +280,12 @@ async def _get_keys_of_keyset(self, url: str, keyset_id: str) -> WalletKeyset: url + f"/keys/{keyset_id_urlsafe}", ) self.raise_on_error(resp) - keys = resp.json() - assert len(keys), Exception("did not receive any keys") + keys_dict = resp.json() + assert len(keys_dict), Exception("did not receive any keys") + keys = KeysResponse.parse_obj(keys_dict) keyset_keys = { int(amt): PublicKey(bytes.fromhex(val), raw=True) - for amt, val in keys.items() + for amt, val in keys.keysets[0].keys.items() } keyset = WalletKeyset(id=keyset_id, public_keys=keyset_keys, mint_url=url) return keyset @@ -307,7 +310,7 @@ async def _get_keyset_ids(self, url: str) -> List[str]: keysets_dict = resp.json() keysets = KeysetsResponse.parse_obj(keysets_dict) assert len(keysets.keysets), Exception("did not receive any keysets") - return keysets.keysets + return [k.id for k in keysets.keysets] @async_set_requests async def _get_info(self, url: str) -> GetInfoResponse: diff --git a/tests/test_mint_api.py b/tests/test_mint_api.py index e62459d5..163c61f8 100644 --- a/tests/test_mint_api.py +++ b/tests/test_mint_api.py @@ -17,7 +17,7 @@ async def test_info(ledger): async def test_api_keys(ledger): response = requests.get(f"{BASE_URL}/keys") assert response.status_code == 200, f"{response.url} {response.status_code}" - assert response.json() == { + assert response.json()["keysets"][0]["keys"] == { str(k): v.serialize().hex() for k, v in ledger.keyset.public_keys.items() } @@ -26,7 +26,7 @@ async def test_api_keys(ledger): async def test_api_keysets(ledger): response = requests.get(f"{BASE_URL}/keysets") assert response.status_code == 200, f"{response.url} {response.status_code}" - assert response.json()["keysets"] == list(ledger.keysets.keysets.keys()) + assert response.json()["keysets"][0]["id"] == list(ledger.keysets.keysets.keys())[0] @pytest.mark.asyncio @@ -35,7 +35,7 @@ async def test_api_keyset_keys(ledger): f"{BASE_URL}/keys/{'1cCNIAZ2X/w1'.replace('/', '_').replace('+', '-')}" ) assert response.status_code == 200, f"{response.url} {response.status_code}" - assert response.json() == { + assert response.json()["keysets"][0]["keys"] == { str(k): v.serialize().hex() for k, v in ledger.keyset.public_keys.items() }