diff --git a/views_api.py b/views_api.py index 0e78bc3..9e0ce8b 100644 --- a/views_api.py +++ b/views_api.py @@ -4,7 +4,7 @@ from typing import List, Optional from fastapi import Depends from fastapi.exceptions import HTTPException -from lnbits.core.crud import get_account, update_account +from lnbits.core.crud import get_account from lnbits.core.services import websocket_updater from lnbits.decorators import ( WalletTypeInfo, @@ -12,7 +12,6 @@ from lnbits.decorators import ( require_invoice_key, ) from lnbits.utils.exchange_rates import currencies -from lnbits.utils.nostr import generate_keypair from loguru import logger from . import nostr_client, nostrmarket_ext @@ -101,21 +100,32 @@ async def _auto_create_merchant( ) -> Merchant: """ Lazy fallback: provision a merchant from the user's account keypair when - the LNbits-side eager provisioning didn't run (e.g., older accounts, or - upstream LNbits without our signup hook). + the LNbits-side eager provisioning didn't run. Delegates to services.provision_merchant — the canonical implementation. + + Pre-cascade bridge state (see aiolabs/nostrmarket#5): + After aiolabs/lnbits#17 m002 lands, `accounts.prvkey` is fail-closed + NULL'd for migrated accounts (the cleartext nsec lives encrypted in + `signer_config`, owned by the core signer abstraction). Auto-provision + cannot extract that cleartext to copy into `merchants.private_key`, + so this path fails-closed when prvkey is missing. The proper fix is + phase A (envelope-encrypt `merchants.private_key` → `signer_blob`) + followed by phase B (route `Merchant.sign_hash` through core's + `NostrSigner`) per aiolabs/nostrmarket#5. Until then, migrated + accounts must explicitly provision a merchant through the future + phase-A-aware flow. + + The previous regenerate-and-write-back block (generated a fresh + keypair and stored it into the account) was removed because it + would silently undo m002's NULL'ing. """ account = await get_account(wallet.wallet.user) assert account, "User account not found" - - # In our fork, accounts always have keypairs. Generate as fallback only - # if somehow missing (e.g., upstream LNbits where this isn't auto-set). - if not account.pubkey or not account.prvkey: - private_key, public_key = generate_keypair() - account.pubkey = public_key - account.prvkey = private_key - await update_account(account) + assert account.pubkey and account.prvkey, ( + "Account has no plaintext Nostr keypair available for merchant " + "provisioning (see aiolabs/nostrmarket#5 for the phase A/B fix)" + ) merchant = await provision_merchant( user_id=wallet.wallet.user, @@ -245,8 +255,13 @@ async def api_migrate_merchant_keys( assert merchant.id == merchant_id, "Wrong merchant ID" account = await get_account(wallet.wallet.user) + # account.prvkey is fail-closed NULL'd by aiolabs/lnbits#17 m002 + # for migrated accounts. Rotation cannot copy a cleartext nsec + # into merchants.private_key until phase A lands — see + # aiolabs/nostrmarket#5 for the migration plan. assert account and account.pubkey and account.prvkey, ( - "Account has no Nostr keypair" + "Account has no plaintext Nostr keypair available for key " + "rotation (see aiolabs/nostrmarket#5 for the phase A/B fix)" ) if account.pubkey == merchant.public_key: