feat(v2): bootstrap consumer task — auto-populate cassette_configs (#29 v1)
Long-running task wired into satmachineadmin_start that subscribes to kind-30078 bitspire-cassettes-state:<atm_pubkey_hex> events from every active machine's ATM and upserts cassette_configs via apply_bootstrap_state on receipt. Pairs with bitspire's one-shot bootstrap publish in aiolabs/lamassu-next#56 — operator's first config publish then validates against a non-empty denomination set. Pattern mirrors wait_for_paid_invoices (try/except per event, never lets the loop die). Uses the same nostr_client.relay_manager singleton that cassette_transport.publish_to_atm uses, just on the subscribe side. Implementation: poll the singleton NostrRouter.received_subscription_events dict keyed by our subscription_id (satmachineadmin-cassette-bootstrap). This is the same drain pattern nostrclient's per-WebSocket NostrRouter uses; since we use a distinct sub_id, no cross-contamination with WebSocket-connected clients of nostrclient. Filter is re-derived from active machines each tick — newly-added machines start receiving bootstrap events without an LNbits restart. Soft-fail surfaces (none crash the listener): - nostrclient extension not installed → log + 30s backoff - inbound event sig-verify fails → log + skip - sender pubkey not in dca_machines → log + skip (relay noise) - operator privkey not on file → log + skip - NIP-44 v2 decrypt / payload validation fails → log + skip - apply_bootstrap_state error → log + skip Per-event handler routes to the right operator's privkey by looking up the machine via get_machine_by_atm_pubkey_hex (O(N) over active machines — fine for small fleets; if fleets grow, normalize machine_npub at write + add an index). CRUD additions: - list_all_active_machines: cross-operator query for the subscription filter - get_machine_by_atm_pubkey_hex: route inbound events to the right machine row + operator account; accepts hex or bech32 storage 14 tests in test_cassette_state_consumer.py covering: - decrypt_and_parse_state_event happy path + 6 negative paths (tamper, wrong privkey, malformed pubkey, missing fields, garbage JSON, wrong-shape payload) - d-tag construction regression guard (REGRESSION GUARD: d-tag uses ATM hex pubkey not internal UUID — pins the load-bearing detail from coord-log 11:50Z) - build_state_d_tags_for_machines + bech32 → hex canonicalisation Full handler dispatch (verify_event → get_machine_by_atm_pubkey_hex → apply_bootstrap_state) needs a live LNbits DB; smoke-tested manually per the existing project convention. Total: 146 passed, 1 skipped (cross-test fixture pending), 1 pre-existing async-plugin failure unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b9d5ea3c57
commit
e57a73083e
4 changed files with 535 additions and 1 deletions
10
__init__.py
10
__init__.py
|
|
@ -5,7 +5,7 @@ from lnbits.tasks import create_permanent_unique_task
|
|||
from loguru import logger
|
||||
|
||||
from .crud import db
|
||||
from .tasks import wait_for_paid_invoices
|
||||
from .tasks import wait_for_cassette_state_events, wait_for_paid_invoices
|
||||
from .views import satmachineadmin_generic_router
|
||||
from .views_api import satmachineadmin_api_router
|
||||
|
||||
|
|
@ -42,6 +42,14 @@ def satmachineadmin_start():
|
|||
"ext_satmachineadmin", wait_for_paid_invoices
|
||||
)
|
||||
scheduled_tasks.append(invoice_task)
|
||||
# Cassette bootstrap consumer (#29 v1) — subscribes to
|
||||
# bitspire-cassettes-state events from each active ATM and upserts
|
||||
# cassette_configs on receipt. Soft-fails if nostrclient isn't
|
||||
# installed (logs + backs off, never crashes).
|
||||
cassette_task = create_permanent_unique_task(
|
||||
"ext_satmachineadmin_cassette_bootstrap", wait_for_cassette_state_events
|
||||
)
|
||||
scheduled_tasks.append(cassette_task)
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue