Files
MicroPythonOS/draft_code/nwc-demo.py
T

215 lines
8.8 KiB
Python
Raw Normal View History

2025-06-13 09:55:17 +02:00
import json
import ssl
import time
import sys
from nostr.relay_manager import RelayManager
from nostr.message_type import ClientMessageType
from nostr.filter import Filter, Filters
from nostr.event import EncryptedDirectMessage
from nostr.key import PrivateKey
def parse_nwc_url(nwc_url):
"""Parse Nostr Wallet Connect URL to extract pubkey, relay, secret, and lud16."""
print(f"DEBUG: Starting to parse NWC URL: {nwc_url}")
try:
# Remove 'nostr+walletconnect://' or 'nwc:' prefix
if nwc_url.startswith('nostr+walletconnect://'):
print(f"DEBUG: Removing 'nostr+walletconnect://' prefix")
nwc_url = nwc_url[22:]
elif nwc_url.startswith('nwc:'):
print(f"DEBUG: Removing 'nwc:' prefix")
nwc_url = nwc_url[4:]
else:
print(f"DEBUG: No recognized prefix found in URL")
raise ValueError("Invalid NWC URL: missing 'nostr+walletconnect://' or 'nwc:' prefix")
print(f"DEBUG: URL after prefix removal: {nwc_url}")
# Split into pubkey and query params
parts = nwc_url.split('?')
pubkey = parts[0]
print(f"DEBUG: Extracted pubkey: {pubkey}")
# Validate pubkey (should be 64 hex characters)
if len(pubkey) != 64 or not all(c in '0123456789abcdef' for c in pubkey):
raise ValueError("Invalid NWC URL: pubkey must be 64 hex characters")
# Extract relay, secret, and lud16 from query params
relay = None
secret = None
lud16 = None
if len(parts) > 1:
print(f"DEBUG: Query parameters found: {parts[1]}")
params = parts[1].split('&')
for param in params:
if param.startswith('relay='):
relay = param[6:]
print(f"DEBUG: Extracted relay: {relay}")
elif param.startswith('secret='):
secret = param[7:]
print(f"DEBUG: Extracted secret: {secret}")
elif param.startswith('lud16='):
lud16 = param[6:]
print(f"DEBUG: Extracted lud16: {lud16}")
else:
print(f"DEBUG: No query parameters found")
if not pubkey or not relay or not secret:
raise ValueError("Invalid NWC URL: missing required fields (pubkey, relay, or secret)")
# Validate secret (should be 64 hex characters)
if len(secret) != 64 or not all(c in '0123456789abcdef' for c in secret):
raise ValueError("Invalid NWC URL: secret must be 64 hex characters")
return {
'relay': relay,
'pubkey': pubkey,
'secret': secret,
'lud16': lud16
}
except Exception as e:
print(f"DEBUG: Error parsing NWC URL: {e}")
sys.exit(1)
def get_balance(nwc_url):
"""Get balance using Nostr Wallet Connect."""
print(f"DEBUG: Starting get_balance with NWC URL: {nwc_url}")
# Parse NWC URL
nwc_data = parse_nwc_url(nwc_url)
relay = nwc_data['relay']
wallet_pubkey = nwc_data['pubkey']
secret = nwc_data['secret']
lud16 = nwc_data['lud16']
print(f"DEBUG: Parsed NWC data - Relay: {relay}, Pubkey: {wallet_pubkey}, Secret: {secret}, lud16: {lud16}")
# Initialize private key from secret (assuming it's a hex key)
try:
#print(f"DEBUG: Initializing private key from secret")
private_key = PrivateKey(bytes.fromhex(secret))
print(f"DEBUG: Private key initialized, public key: {private_key.public_key.hex()}")
except Exception as e:
print(f"DEBUG: Error initializing private key: {e}")
sys.exit(1)
# Create get_balance request
balance_request = {
"method": "get_balance",
"params": {}
}
print(f"DEBUG: Created balance request: {balance_request}")
#balance_request_string = json.dumps(balance_request)
#encrypted = private_key.encrypt_message(balance_request_string, wallet_pubkey)
#print(f"\n\n\nencryption returned: {encrypted}")
#decrypted = private_key.decrypt_message(encrypted, wallet_pubkey)
#print(f"\n\n\ndecryption returned: {decrypted}\n\n\n")
#decrypted = private_key.decrypt_message(encrypted, wallet_pubkey)
#print(f"\n\n\ndecryption from nak returned: {decrypted}\n\n\n")
# padding error
# Create encrypted DM with the balance request
print(f"DEBUG: Creating encrypted DM to wallet pubkey: {wallet_pubkey}")
dm = EncryptedDirectMessage(
recipient_pubkey=wallet_pubkey,
cleartext_content=json.dumps(balance_request)
)
print(f"DEBUG: Signing DM {json.dumps(dm)} with private key")
private_key.sign_event(dm) # sign also does encryption if it's a encrypted dm
print(f"DEBUG: DM created with ID: {dm.id}")
# Set up relay manager
print(f"DEBUG: Setting up relay manager with relay: {relay}")
relay_manager = RelayManager()
relay_manager.add_relay(relay)
print(f"DEBUG: Opening relay connections")
relay_manager.open_connections({"cert_reqs": ssl.CERT_NONE})
time.sleep(2) # Allow connections to open
# Check for relay connection notices
print(f"DEBUG: Checking for relay notices")
while relay_manager.message_pool.has_notices():
notice = relay_manager.message_pool.get_notice()
print(f"DEBUG: Relay notice: {notice.content}")
# Set up subscription to receive response
subscription_id = "nwc_balance_" + str(round(time.time()))
#print(f"DEBUG: Setting up subscription with ID: {subscription_id}")
filters = Filters([Filter(
kinds=[23195], # NWC replies
authors=[wallet_pubkey],
pubkey_refs=[private_key.public_key.hex()]
)])
#print(f"DEBUG: Subscription filters: {filters.to_json_array()}")
relay_manager.add_subscription(subscription_id, filters)
# Publish request
print(f"DEBUG: Publishing subscription request")
request_message = [ClientMessageType.REQUEST, subscription_id]
request_message.extend(filters.to_json_array())
relay_manager.publish_message(json.dumps(request_message))
print(f"DEBUG: Publishing encrypted DM")
relay_manager.publish_event(dm)
# only accept events after the time it was published
after_time = time.time()
if sys.platform == "esp32":
# on esp32, it needs this correction:
after_time += 946684800
after_time -= 60 # go back a bit because server clocks might be drifting
print(f"will only consider events after {after_time}")
# Wait for response
print(f"DEBUG: Waiting for response...")
print(f"starting at {time.localtime()}")
start_time = time.time()
balance = None
while time.time() - start_time < 60 * 2:
while relay_manager.message_pool.has_events():
print(f"DEBUG: Event received from message pool")
event_msg = relay_manager.message_pool.get_event()
event_created_at = event_msg.event.created_at
print(f"Received at {time.localtime()} a message with timestamp {event_created_at}")
if event_created_at < after_time:
print("Skipping event because it's too old!")
continue
#print(f"event_msg content {event_msg.event.content}")
try:
#print(f"DEBUG: Decrypting event from public_key: {event_msg.event.public_key}")
decrypted_content = private_key.decrypt_message(
event_msg.event.content,
event_msg.event.public_key
)
print(f"DEBUG: Decrypted content: {decrypted_content}")
response = json.loads(decrypted_content)
print(f"DEBUG: Parsed response: {response}")
if response.get("method") == "get_balance":
balance = response.get("result", {}).get("balance")
print(f"DEBUG: Balance found: {balance} satoshis")
break
except Exception as e:
print(f"DEBUG: Error processing response: {e}")
if balance is not None:
break
time.sleep(1)
print(f"finished at {time.localtime()}")
# Close connections
print(f"DEBUG: Closing relay connections")
relay_manager.close_connections()
if balance is not None:
print(f"Balance: {balance} satoshis")
else:
print("No balance response received or request timed out")
# Example usage
#nwc_url = "nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.getalby.com/v1&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c&lud16=moritz@getalby.com"
#nwc_url = "nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss://relay.getalby.com/v1&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c&lud16=moritz@getalby.com"
print(f"Processing NWC URL: {nwc_url}")
get_balance(nwc_url)