Wallets/unhashed_description (#870)
* new argument: unhashed_description * accept in api * set unhashed_description for memo case * bolt11.py: dont be like CLN, accept the hash * send hash to lnd in b64 * fix cln * skip descr_hash for cln * skip * format
This commit is contained in:
parent
3457ff101e
commit
e5d8c500d2
|
@ -216,7 +216,7 @@ def lnencode(addr, privkey):
|
|||
expirybits = expirybits[5:]
|
||||
data += tagged("x", expirybits)
|
||||
elif k == "h":
|
||||
data += tagged_bytes("h", hashlib.sha256(v.encode("utf-8")).digest())
|
||||
data += tagged_bytes("h", v)
|
||||
elif k == "n":
|
||||
data += tagged_bytes("n", v)
|
||||
else:
|
||||
|
|
|
@ -54,6 +54,7 @@ async def create_invoice(
|
|||
amount: int, # in satoshis
|
||||
memo: str,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
extra: Optional[Dict] = None,
|
||||
webhook: Optional[str] = None,
|
||||
internal: Optional[bool] = False,
|
||||
|
@ -65,7 +66,10 @@ async def create_invoice(
|
|||
wallet = FAKE_WALLET if internal else WALLET
|
||||
|
||||
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
|
||||
amount=amount, memo=invoice_memo, description_hash=description_hash
|
||||
amount=amount,
|
||||
memo=invoice_memo,
|
||||
description_hash=description_hash,
|
||||
unhashed_description=unhashed_description,
|
||||
)
|
||||
if not ok:
|
||||
raise InvoiceFailure(error_message or "unexpected backend error.")
|
||||
|
|
|
@ -141,6 +141,7 @@ class CreateInvoiceData(BaseModel):
|
|||
memo: Optional[str] = None
|
||||
unit: Optional[str] = "sat"
|
||||
description_hash: Optional[str] = None
|
||||
unhashed_description: Optional[str] = None
|
||||
lnurl_callback: Optional[str] = None
|
||||
lnurl_balance_check: Optional[str] = None
|
||||
extra: Optional[dict] = None
|
||||
|
@ -152,9 +153,15 @@ class CreateInvoiceData(BaseModel):
|
|||
async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
||||
if data.description_hash:
|
||||
description_hash = unhexlify(data.description_hash)
|
||||
unhashed_description = b""
|
||||
memo = ""
|
||||
elif data.unhashed_description:
|
||||
unhashed_description = unhexlify(data.unhashed_description)
|
||||
description_hash = b""
|
||||
memo = ""
|
||||
else:
|
||||
description_hash = b""
|
||||
unhashed_description = b""
|
||||
memo = data.memo or LNBITS_SITE_TITLE
|
||||
if data.unit == "sat":
|
||||
amount = int(data.amount)
|
||||
|
@ -170,6 +177,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
|||
amount=amount,
|
||||
memo=memo,
|
||||
description_hash=description_hash,
|
||||
unhashed_description=unhashed_description,
|
||||
extra=data.extra,
|
||||
webhook=data.webhook,
|
||||
internal=data.internal,
|
||||
|
|
|
@ -46,12 +46,19 @@ class ClicheWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
if description_hash:
|
||||
description_hash_hashed = hashlib.sha256(description_hash).hexdigest()
|
||||
if unhashed_description or description_hash:
|
||||
description_hash_str = (
|
||||
description_hash.hex()
|
||||
if description_hash
|
||||
else hashlib.sha256(unhashed_description).hexdigest()
|
||||
if unhashed_description
|
||||
else None
|
||||
)
|
||||
ws = create_connection(self.endpoint)
|
||||
ws.send(
|
||||
f"create-invoice --msatoshi {amount*1000} --description_hash {description_hash_hashed}"
|
||||
f"create-invoice --msatoshi {amount*1000} --description_hash {description_hash_str}"
|
||||
)
|
||||
r = ws.recv()
|
||||
else:
|
||||
|
|
|
@ -82,21 +82,24 @@ class CoreLightningWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
label = "lbl{}".format(random.random())
|
||||
msat: int = int(amount * 1000)
|
||||
try:
|
||||
if description_hash and not self.supports_description_hash:
|
||||
if description_hash:
|
||||
raise Unsupported("description_hash")
|
||||
if unhashed_description and not self.supports_description_hash:
|
||||
raise Unsupported("unhashed_description")
|
||||
r = self.ln.invoice(
|
||||
msatoshi=msat,
|
||||
label=label,
|
||||
description=description_hash.decode("utf-8")
|
||||
if description_hash
|
||||
description=unhashed_description.decode("utf-8")
|
||||
if unhashed_description
|
||||
else memo,
|
||||
exposeprivatechannels=True,
|
||||
deschashonly=True
|
||||
if description_hash
|
||||
if unhashed_description
|
||||
else False, # we can't pass None here
|
||||
)
|
||||
|
||||
|
|
|
@ -69,11 +69,14 @@ class EclairWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
|
||||
data: Dict = {"amountMsat": amount * 1000}
|
||||
if description_hash:
|
||||
data["description_hash"] = hashlib.sha256(description_hash).hexdigest()
|
||||
data["description_hash"] = description_hash.hex()
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
|
||||
else:
|
||||
data["description"] = memo or ""
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class FakeWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
# we set a default secret since FakeWallet is used for internal=True invoices
|
||||
# and the user might not have configured a secret yet
|
||||
|
@ -61,7 +62,10 @@ class FakeWallet(Wallet):
|
|||
data["timestamp"] = datetime.now().timestamp()
|
||||
if description_hash:
|
||||
data["tags_set"] = ["h"]
|
||||
data["description_hash"] = description_hash.decode("utf-8")
|
||||
data["description_hash"] = description_hash
|
||||
elif unhashed_description:
|
||||
data["tags_set"] = ["d"]
|
||||
data["description_hash"] = hashlib.sha256(unhashed_description).digest()
|
||||
else:
|
||||
data["tags_set"] = ["d"]
|
||||
data["memo"] = memo
|
||||
|
|
|
@ -57,10 +57,13 @@ class LNbitsWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
data: Dict = {"out": False, "amount": amount}
|
||||
if description_hash:
|
||||
data["description_hash"] = hashlib.sha256(description_hash).hexdigest()
|
||||
data["description_hash"] = description_hash.hex()
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
|
||||
else:
|
||||
data["memo"] = memo or ""
|
||||
|
||||
|
|
|
@ -134,14 +134,15 @@ class LndWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
params: Dict = {"value": amount, "expiry": 600, "private": True}
|
||||
|
||||
if description_hash:
|
||||
params["description_hash"] = description_hash
|
||||
elif unhashed_description:
|
||||
params["description_hash"] = hashlib.sha256(
|
||||
description_hash
|
||||
unhashed_description
|
||||
).digest() # as bytes directly
|
||||
|
||||
else:
|
||||
params["memo"] = memo or ""
|
||||
|
||||
|
|
|
@ -73,11 +73,17 @@ class LndRestWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
data: Dict = {"value": amount, "private": True}
|
||||
if description_hash:
|
||||
data["description_hash"] = base64.b64encode(description_hash).decode(
|
||||
"ascii"
|
||||
)
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = base64.b64encode(
|
||||
hashlib.sha256(description_hash).digest()
|
||||
hashlib.sha256(unhashed_description).digest()
|
||||
).decode("ascii")
|
||||
else:
|
||||
data["memo"] = memo or ""
|
||||
|
|
|
@ -52,10 +52,14 @@ class LNPayWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
data: Dict = {"num_satoshis": f"{amount}"}
|
||||
if description_hash:
|
||||
data["description_hash"] = hashlib.sha256(description_hash).hexdigest()
|
||||
data["description_hash"] = description_hash.hex()
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
|
||||
else:
|
||||
data["memo"] = memo or ""
|
||||
|
||||
|
|
|
@ -52,10 +52,14 @@ class LntxbotWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
data: Dict = {"amt": str(amount)}
|
||||
if description_hash:
|
||||
data["description_hash"] = hashlib.sha256(description_hash).hexdigest()
|
||||
data["description_hash"] = description_hash.hex()
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
|
||||
else:
|
||||
data["memo"] = memo or ""
|
||||
|
||||
|
|
|
@ -54,8 +54,10 @@ class OpenNodeWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
if description_hash:
|
||||
if description_hash or unhashed_description:
|
||||
raise Unsupported("description_hash")
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
|
|
|
@ -93,6 +93,8 @@ class SparkWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
label = "lbs{}".format(random.random())
|
||||
checking_id = label
|
||||
|
@ -102,7 +104,13 @@ class SparkWallet(Wallet):
|
|||
r = await self.invoicewithdescriptionhash(
|
||||
msatoshi=amount * 1000,
|
||||
label=label,
|
||||
description_hash=hashlib.sha256(description_hash).hexdigest(),
|
||||
description_hash=description_hash.hex(),
|
||||
)
|
||||
elif unhashed_description:
|
||||
r = await self.invoicewithdescriptionhash(
|
||||
msatoshi=amount * 1000,
|
||||
label=label,
|
||||
description_hash=hashlib.sha256(unhashed_description).hexdigest(),
|
||||
)
|
||||
else:
|
||||
r = await self.invoice(
|
||||
|
|
|
@ -18,6 +18,7 @@ class VoidWallet(Wallet):
|
|||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
raise Unsupported("")
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from lnbits.core.views.api import (
|
|||
api_payment,
|
||||
api_payments_create_invoice,
|
||||
)
|
||||
from lnbits.settings import wallet_class
|
||||
|
||||
from ...helpers import get_random_invoice_data
|
||||
|
||||
|
@ -192,11 +193,32 @@ async def test_api_payment_with_key(invoice, inkey_headers_from):
|
|||
|
||||
|
||||
# check POST /api/v1/payments: invoice creation with a description hash
|
||||
@pytest.mark.skipif(
|
||||
wallet_class.__name__ in ["CoreLightningWallet"],
|
||||
reason="wallet does not support description_hash",
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_invoice_with_description_hash(client, inkey_headers_to):
|
||||
data = await get_random_invoice_data()
|
||||
descr_hash = hashlib.sha256("asdasdasd".encode("utf-8")).hexdigest()
|
||||
data["description_hash"] = "asdasdasd".encode("utf-8").hex()
|
||||
data["description_hash"] = descr_hash
|
||||
|
||||
response = await client.post(
|
||||
"/api/v1/payments", json=data, headers=inkey_headers_to
|
||||
)
|
||||
invoice = response.json()
|
||||
|
||||
invoice_bolt11 = bolt11.decode(invoice["payment_request"])
|
||||
assert invoice_bolt11.description_hash == descr_hash
|
||||
assert invoice_bolt11.description is None
|
||||
return invoice
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_invoice_with_unhashed_description(client, inkey_headers_to):
|
||||
data = await get_random_invoice_data()
|
||||
descr_hash = hashlib.sha256("asdasdasd".encode("utf-8")).hexdigest()
|
||||
data["unhashed_description"] = "asdasdasd".encode("utf-8").hex()
|
||||
|
||||
response = await client.post(
|
||||
"/api/v1/payments", json=data, headers=inkey_headers_to
|
||||
|
|
Loading…
Reference in New Issue
Block a user