mirror of
https://github.com/aljazceru/nutshell.git
synced 2026-02-03 07:44:21 +01:00
lightning: add fakewallet (#134)
* lightning: add fakewallet * make format * fix mypy * make backend configurable * weird mypy
This commit is contained in:
@@ -21,6 +21,9 @@ TOR=TRUE
|
||||
|
||||
MINT_PRIVATE_KEY=supersecretprivatekey
|
||||
|
||||
# Supported: LNbitsWallet, FakeWallet
|
||||
MINT_LIGHTNING_BACKEND=LNbitsWallet
|
||||
|
||||
MINT_SERVER_HOST=127.0.0.1
|
||||
MINT_SERVER_PORT=3338
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ MINT_URL = env.str("MINT_URL", default=None)
|
||||
MINT_HOST = env.str("MINT_HOST", default="8333.space")
|
||||
MINT_PORT = env.int("MINT_PORT", default=3338)
|
||||
|
||||
MINT_LIGHTNING_BACKEND = env.str("MINT_LIGHTNING_BACKEND", default="FakeWallet")
|
||||
MINT_DATABASE = env.str("MINT_DATABASE", default="data/mint")
|
||||
|
||||
if not MINT_URL:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# from cashu.lightning.lnbits import LNbitsWallet
|
||||
|
||||
# WALLET = LNbitsWallet()
|
||||
# type: ignore
|
||||
from .fake import FakeWallet
|
||||
from .lnbits import LNbitsWallet
|
||||
|
||||
100
cashu/lightning/fake.py
Normal file
100
cashu/lightning/fake.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import asyncio
|
||||
import hashlib
|
||||
import random
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator, Dict, Optional, Set
|
||||
|
||||
from cashu.core.bolt11 import Invoice, decode, encode
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
||||
class FakeWallet(Wallet):
|
||||
"""https://github.com/lnbits/lnbits"""
|
||||
|
||||
queue: asyncio.Queue = asyncio.Queue(0)
|
||||
paid_invoices: Set[str] = set()
|
||||
secret: str = "FAKEWALLET SECRET"
|
||||
privkey: str = hashlib.pbkdf2_hmac(
|
||||
"sha256",
|
||||
secret.encode(),
|
||||
("FakeWallet").encode(),
|
||||
2048,
|
||||
32,
|
||||
).hex()
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
return StatusResponse(None, 1337)
|
||||
|
||||
async def create_invoice(
|
||||
self,
|
||||
amount: int,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
data: Dict = {
|
||||
"out": False,
|
||||
"amount": amount * 1000,
|
||||
"currency": "bc",
|
||||
"privkey": self.privkey,
|
||||
"memo": memo,
|
||||
"description_hash": b"",
|
||||
"description": "",
|
||||
"fallback": None,
|
||||
"expires": kwargs.get("expiry"),
|
||||
"timestamp": datetime.now().timestamp(),
|
||||
"route": None,
|
||||
"tags_set": [],
|
||||
}
|
||||
if description_hash:
|
||||
data["tags_set"] = ["h"]
|
||||
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
|
||||
data["description"] = memo
|
||||
randomHash = (
|
||||
self.privkey[:6]
|
||||
+ hashlib.sha256(str(random.getrandbits(256)).encode()).hexdigest()[6:]
|
||||
)
|
||||
data["paymenthash"] = randomHash
|
||||
payment_request = encode(data)
|
||||
checking_id = randomHash
|
||||
|
||||
return InvoiceResponse(True, checking_id, payment_request)
|
||||
|
||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||
invoice = decode(bolt11)
|
||||
|
||||
if invoice.payment_hash[:6] == self.privkey[:6]:
|
||||
await self.queue.put(invoice)
|
||||
self.paid_invoices.add(invoice.payment_hash)
|
||||
return PaymentResponse(True, invoice.payment_hash, 0)
|
||||
else:
|
||||
return PaymentResponse(
|
||||
ok=False, error_message="Only internal invoices can be used!"
|
||||
)
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
|
||||
paid = checking_id in self.paid_invoices
|
||||
return PaymentStatus(paid or None)
|
||||
|
||||
async def get_payment_status(self, _: str) -> PaymentStatus:
|
||||
return PaymentStatus(None)
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
while True:
|
||||
value: Invoice = await self.queue.get()
|
||||
yield value.payment_hash
|
||||
@@ -1,5 +1,4 @@
|
||||
# type: ignore
|
||||
from os import getenv
|
||||
from typing import Dict, Optional
|
||||
|
||||
import requests
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
# startup routine of the standalone app. These are the steps that need
|
||||
# to be taken by external apps importing the cashu mint.
|
||||
|
||||
import asyncio
|
||||
import importlib
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from cashu.core.db import Database
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import CASHU_DIR, LIGHTNING, MINT_DATABASE, MINT_PRIVATE_KEY
|
||||
from cashu.core.settings import (
|
||||
CASHU_DIR,
|
||||
LIGHTNING,
|
||||
MINT_DATABASE,
|
||||
MINT_LIGHTNING_BACKEND,
|
||||
MINT_PRIVATE_KEY,
|
||||
)
|
||||
from cashu.lightning.fake import FakeWallet # type: ignore
|
||||
from cashu.lightning.lnbits import LNbitsWallet # type: ignore
|
||||
from cashu.mint import migrations
|
||||
from cashu.mint.ledger import Ledger
|
||||
|
||||
wallets_module = importlib.import_module("cashu.lightning")
|
||||
LIGHTNING_BACKEND = getattr(wallets_module, MINT_LIGHTNING_BACKEND)()
|
||||
|
||||
ledger = Ledger(
|
||||
db=Database("mint", MINT_DATABASE),
|
||||
seed=MINT_PRIVATE_KEY,
|
||||
derivation_path="0/0/0/0",
|
||||
lightning=LNbitsWallet(),
|
||||
lightning=LIGHTNING_BACKEND,
|
||||
)
|
||||
|
||||
|
||||
@@ -27,6 +37,7 @@ async def start_mint_init():
|
||||
await ledger.init_keysets()
|
||||
|
||||
if LIGHTNING:
|
||||
logger.info(f"Using backend: {MINT_LIGHTNING_BACKEND}")
|
||||
error_message, balance = await ledger.lightning.status()
|
||||
if error_message:
|
||||
logger.warning(
|
||||
|
||||
Reference in New Issue
Block a user