Allow arbitrary denominations in wallet minting (#684)

- Update `mint` method to use allowed amounts from the mint
- Add `get_allowed_amounts` method to fetch supported denominations
- Modify `test_mint_amounts_wrong_order` to expect new error message

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
Houdini
2025-03-06 20:15:14 +01:00
committed by GitHub
parent 72ac35405c
commit 7b3f5d01d0
3 changed files with 25 additions and 6 deletions

View File

@@ -111,6 +111,21 @@ class WalletTransactions(SupportsDb, SupportsKeysets):
proofs_send = self.coinselect(proofs, amount, include_fees=True) proofs_send = self.coinselect(proofs, amount, include_fees=True)
return self.get_fees_for_proofs(proofs_send) return self.get_fees_for_proofs(proofs_send)
def get_allowed_amounts(self):
"""
Infer the allowed amounts from the current keyset's public keys.
Returns:
List[int]: A sorted list of allowed token amounts for the current keyset.
Raises:
Exception: If no active keyset is set.
"""
if not self.keyset_id or self.keyset_id not in self.keysets:
raise Exception("No active keyset")
return sorted(list(self.keysets[self.keyset_id].public_keys.keys()))
def split_wallet_state(self, amount: int) -> List[int]: def split_wallet_state(self, amount: int) -> List[int]:
"""This function produces an amount split for outputs based on the current state of the wallet. """This function produces an amount split for outputs based on the current state of the wallet.
Its objective is to fill up the wallet so that it reaches `n_target` coins of each amount. Its objective is to fill up the wallet so that it reaches `n_target` coins of each amount.
@@ -125,8 +140,9 @@ class WalletTransactions(SupportsDb, SupportsKeysets):
n_target = settings.wallet_target_amount_count n_target = settings.wallet_target_amount_count
amounts_we_have = [p.amount for p in self.proofs if p.reserved is not True] amounts_we_have = [p.amount for p in self.proofs if p.reserved is not True]
amounts_we_have.sort() amounts_we_have.sort()
# NOTE: Do not assume 2^n here
all_possible_amounts: list[int] = [2**i for i in range(settings.max_order)] all_possible_amounts = self.get_allowed_amounts()
amounts_we_want_ll = [ amounts_we_want_ll = [
[a] * max(0, n_target - amounts_we_have.count(a)) [a] * max(0, n_target - amounts_we_have.count(a))
for a in all_possible_amounts for a in all_possible_amounts

View File

@@ -519,6 +519,8 @@ class Wallet(
await store_bolt11_mint_quote(db=self.db, quote=quote) await store_bolt11_mint_quote(db=self.db, quote=quote)
return quote return quote
async def mint( async def mint(
self, self,
amount: int, amount: int,
@@ -529,7 +531,7 @@ class Wallet(
Args: Args:
amount (int): Total amount of tokens to be minted amount (int): Total amount of tokens to be minted
id (str): Id for looking up the paid Lightning invoice. quote_id (str): Id for looking up the paid Lightning invoice.
split (Optional[List[str]], optional): List of desired amount splits to be minted. Total must sum to `amount`. split (Optional[List[str]], optional): List of desired amount splits to be minted. Total must sum to `amount`.
Raises: Raises:
@@ -543,11 +545,11 @@ class Wallet(
if split: if split:
logger.trace(f"Mint with split: {split}") logger.trace(f"Mint with split: {split}")
assert sum(split) == amount, "split must sum to amount" assert sum(split) == amount, "split must sum to amount"
allowed_amounts = [2**i for i in range(settings.max_order)] allowed_amounts = self.get_allowed_amounts() # Get allowed amounts from the mint
for a in split: for a in split:
if a not in allowed_amounts: if a not in allowed_amounts:
raise Exception( raise Exception(
f"Can only mint amounts with 2^n up to {2**settings.max_order}." f"Can only mint amounts supported by the mint: {allowed_amounts}"
) )
# split based on our wallet state # split based on our wallet state

View File

@@ -221,9 +221,10 @@ async def test_mint_amounts_wrong_order(wallet1: Wallet):
"""Mint amount that is not part in 2^n""" """Mint amount that is not part in 2^n"""
amts = [1, 2, 3] amts = [1, 2, 3]
mint_quote = await wallet1.request_mint(sum(amts)) mint_quote = await wallet1.request_mint(sum(amts))
allowed_amounts = wallet1.get_allowed_amounts()
await assert_err( await assert_err(
wallet1.mint(amount=sum(amts), split=[1, 2, 3], quote_id=mint_quote.quote), wallet1.mint(amount=sum(amts), split=[1, 2, 3], quote_id=mint_quote.quote),
f"Can only mint amounts with 2^n up to {2**settings.max_order}.", f"Can only mint amounts supported by the mint: {allowed_amounts}",
) )