private zaps (still some issues with receiving zaps)

This commit is contained in:
Believethehype
2023-11-26 20:56:48 +01:00
parent 9fc325cc95
commit fe054c76db
3 changed files with 73 additions and 36 deletions

6
bot.py
View File

@@ -234,7 +234,9 @@ class Bot:
# else we create a zap # else we create a zap
else: else:
bolt11 = zap("ai@bitcoinfixesthis.org", amount, "Zap", ptag, etag, self.keys, self.dvm_config) user = get_or_add_user(db=self.dvm_config.DB, npub=nostr_event.pubkey().to_hex(),
client=self.client, config=self.dvm_config)
bolt11 = zap(user.lud16, amount, "Zap", nostr_event, self.keys, self.dvm_config)
if bolt11 == None: if bolt11 == None:
print("Receiver has no Lightning address") print("Receiver has no Lightning address")
return return
@@ -242,7 +244,7 @@ class Bot:
payment_hash = pay_bolt11_ln_bits(bolt11, self.dvm_config) payment_hash = pay_bolt11_ln_bits(bolt11, self.dvm_config)
self.job_list[self.job_list.index(entry)]['is_paid'] = True self.job_list[self.job_list.index(entry)]['is_paid'] = True
print("[" + self.NAME + "] payment_hash: " + payment_hash + print("[" + self.NAME + "] payment_hash: " + payment_hash +
" Forwarding payment of " + amount + " Sats to DVM") " Forwarding payment of " + str(amount) + " Sats to DVM")
except Exception as e: except Exception as e:
print(e) print(e)

11
dvm.py
View File

@@ -53,15 +53,15 @@ class DVM:
self.client.connect() self.client.connect()
zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now()) zap_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_ZAP]).since(Timestamp.now())
bot_dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).authors(self.dvm_config.DM_ALLOWED).since( #bot_dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).authors(self.dvm_config.DM_ALLOWED).since(
Timestamp.now()) # Timestamp.now())
kinds = [EventDefinitions.KIND_NIP90_GENERIC] kinds = [EventDefinitions.KIND_NIP90_GENERIC]
for dvm in self.dvm_config.SUPPORTED_DVMS: for dvm in self.dvm_config.SUPPORTED_DVMS:
if dvm.KIND not in kinds: if dvm.KIND not in kinds:
kinds.append(dvm.KIND) kinds.append(dvm.KIND)
dvm_filter = (Filter().kinds(kinds).since(Timestamp.now())) dvm_filter = (Filter().kinds(kinds).since(Timestamp.now()))
self.client.subscribe([dvm_filter, zap_filter, bot_dm_filter]) self.client.subscribe([dvm_filter, zap_filter])
create_sql_table(self.dvm_config.DB) create_sql_table(self.dvm_config.DB)
admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client) admin_make_database_updates(adminconfig=self.admin_config, dvmconfig=self.dvm_config, client=self.client)
@@ -158,7 +158,6 @@ class DVM:
do_work(nip90_event) do_work(nip90_event)
# if task is directed to us via p tag and user has balance, do the job and update balance # if task is directed to us via p tag and user has balance, do the job and update balance
elif p == Keys.from_sk_str(self.dvm_config.PRIVATE_KEY).public_key().to_hex() and user.balance >= amount: elif p == Keys.from_sk_str(self.dvm_config.PRIVATE_KEY).public_key().to_hex() and user.balance >= amount:
balance = max(user.balance - amount, 0) balance = max(user.balance - amount, 0)
update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance, update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance,
iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted, iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted,
@@ -261,14 +260,14 @@ class DVM:
print("[" + self.dvm_config.NIP89.name + "] " print("[" + self.dvm_config.NIP89.name + "] "
"Someone zapped the result of an exisiting Task. Nice") "Someone zapped the result of an exisiting Task. Nice")
elif not anon: elif not anon:
print("[" + self.dvm_config.NIP89.name + "] Note Zap received for Bot balance: " + print("[" + self.dvm_config.NIP89.name + "] Note Zap received for DVM balance: " +
str(invoice_amount) + " Sats from " + str(user.name)) str(invoice_amount) + " Sats from " + str(user.name))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config) config=self.dvm_config)
# a regular note # a regular note
elif not anon: elif not anon:
print("[" + self.dvm_config.NIP89.name + "] Profile Zap received for Bot balance: " + print("[" + self.dvm_config.NIP89.name + "] Profile Zap received for DVM balance: " +
str(invoice_amount) + " Sats from " + str(user.name)) str(invoice_amount) + " Sats from " + str(user.name))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client, update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config) config=self.dvm_config)

View File

@@ -1,14 +1,17 @@
# LIGHTNING FUNCTIONS # LIGHTNING FUNCTIONS
import json import json
import os
import urllib.parse import urllib.parse
import requests import requests
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from bech32 import bech32_decode, convertbits, bech32_encode from bech32 import bech32_decode, convertbits, bech32_encode
from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag from nostr_sdk import nostr_sdk, PublicKey, SecretKey, Event, EventBuilder, Tag, Keys
from utils.dvmconfig import DVMConfig from utils.dvmconfig import DVMConfig
from utils.nostr_utils import get_event_by_id from utils.nostr_utils import get_event_by_id
import lnurl import lnurl
from hashlib import sha256
def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int: def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int:
@@ -126,6 +129,26 @@ def check_for_zapplepay(pubkey_hex: str, content: str):
return pubkey_hex return pubkey_hex
def enrypt_private_zap_message(message, privatekey, publickey):
# Generate a random IV
shared_secret = nostr_sdk.generate_shared_key(privatekey, publickey)
iv = os.urandom(16)
# Encrypt the message
cipher = AES.new(bytearray(shared_secret), AES.MODE_CBC, bytearray(iv))
utf8message = message.encode('utf-8')
padded_message = pad(utf8message, AES.block_size)
encrypted_msg = cipher.encrypt(padded_message)
encrypted_msg_bech32 = bech32_encode("pzap", convertbits(encrypted_msg, 8, 5, True))
iv_bech32 = bech32_encode("iv", convertbits(iv, 8, 5, True))
print("Encrypted Message:", encrypted_msg_bech32)
print("IV:", iv_bech32)
return encrypted_msg_bech32 + "_" + iv_bech32
def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey): def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey):
shared_secret = nostr_sdk.generate_shared_key(privkey, pubkey) shared_secret = nostr_sdk.generate_shared_key(privkey, pubkey)
if len(shared_secret) != 16 and len(shared_secret) != 32: if len(shared_secret) != 16 and len(shared_secret) != 32:
@@ -150,7 +173,7 @@ def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey)
return str(ex) return str(ex)
def zap(lud16: str, amount: int, content, recipient_pubkey, zapped_event, keys, dvm_config): def zap(lud16: str, amount: int, content, zapped_event: Event, keys, dvm_config, zaptype="public"):
if lud16.startswith("LNURL") or lud16.startswith("lnurl"): if lud16.startswith("LNURL") or lud16.startswith("lnurl"):
url = lnurl.decode(lud16) url = lnurl.decode(lud16)
elif '@' in lud16: # LNaddress elif '@' in lud16: # LNaddress
@@ -164,12 +187,25 @@ def zap(lud16: str, amount: int, content, recipient_pubkey, zapped_event, keys,
encoded_lnurl = lnurl.encode(url) encoded_lnurl = lnurl.encode(url)
amount_tag = Tag.parse(['amount', str(amount * 1000)]) amount_tag = Tag.parse(['amount', str(amount * 1000)])
relays_tag = Tag.parse(['relays', str(dvm_config.RELAY_LIST)]) relays_tag = Tag.parse(['relays', str(dvm_config.RELAY_LIST)])
p_tag = Tag.parse(['p', recipient_pubkey]) p_tag = Tag.parse(['p', zapped_event.pubkey().to_hex()])
e_tag = Tag.parse(['e', zapped_event]) e_tag = Tag.parse(['e', zapped_event.id().to_hex()])
lnurl_tag = Tag.parse(['lnurl', encoded_lnurl]) lnurl_tag = Tag.parse(['lnurl', encoded_lnurl])
tags = [amount_tag, relays_tag, p_tag, e_tag, lnurl_tag]
if zaptype == "private":
key_str = keys.secret_key().to_hex() + zapped_event.id().to_hex() + str(zapped_event.created_at().as_secs())
encryption_key = sha256(key_str.encode('utf-8')).hexdigest()
zap_request = EventBuilder(9733, content,
[p_tag, e_tag]).to_event(keys).as_json()
keys = Keys.from_sk_str(encryption_key)
encrypted_content = enrypt_private_zap_message(zap_request, keys.secret_key(), zapped_event.pubkey())
anon_tag = Tag.parse(['anon', encrypted_content])
tags.append(anon_tag)
content = ""
zap_request = EventBuilder(9734, content, zap_request = EventBuilder(9734, content,
[amount_tag, relays_tag, p_tag, e_tag, lnurl_tag]).to_event(keys).as_json() tags).to_event(keys).as_json()
response = requests.get(callback + "?amount=" + str(int(amount) * 1000) + "&nostr=" + urllib.parse.quote_plus( response = requests.get(callback + "?amount=" + str(int(amount) * 1000) + "&nostr=" + urllib.parse.quote_plus(
zap_request) + "&lnurl=" + encoded_lnurl) zap_request) + "&lnurl=" + encoded_lnurl)