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:
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:
print("Receiver has no Lightning address")
return
@ -242,7 +244,7 @@ class Bot:
payment_hash = pay_bolt11_ln_bits(bolt11, self.dvm_config)
self.job_list[self.job_list.index(entry)]['is_paid'] = True
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:
print(e)

11
dvm.py
View File

@ -53,15 +53,15 @@ class DVM:
self.client.connect()
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(
Timestamp.now())
#bot_dm_filter = Filter().pubkey(pk).kinds([EventDefinitions.KIND_DM]).authors(self.dvm_config.DM_ALLOWED).since(
# Timestamp.now())
kinds = [EventDefinitions.KIND_NIP90_GENERIC]
for dvm in self.dvm_config.SUPPORTED_DVMS:
if dvm.KIND not in kinds:
kinds.append(dvm.KIND)
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)
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)
# 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:
balance = max(user.balance - amount, 0)
update_sql_table(db=self.dvm_config.DB, npub=user.npub, balance=balance,
iswhitelisted=user.iswhitelisted, isblacklisted=user.isblacklisted,
@ -261,14 +260,14 @@ class DVM:
print("[" + self.dvm_config.NIP89.name + "] "
"Someone zapped the result of an exisiting Task. Nice")
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))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config)
# a regular note
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))
update_user_balance(self.dvm_config.DB, sender, invoice_amount, client=self.client,
config=self.dvm_config)

View File

@ -1,14 +1,17 @@
# LIGHTNING FUNCTIONS
import json
import os
import urllib.parse
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
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.nostr_utils import get_event_by_id
import lnurl
from hashlib import sha256
def parse_amount_from_bolt11_invoice(bolt11_invoice: str) -> int:
@ -50,26 +53,26 @@ def parse_zap_event_tags(zap_event, keys, name, client, config):
elif tag.as_vec()[0] == 'e':
zapped_event = get_event_by_id(tag.as_vec()[1], client=client, config=config)
elif tag.as_vec()[0] == 'description':
zap_request_event = Event.from_json(tag.as_vec()[1])
sender = check_for_zapplepay(zap_request_event.pubkey().to_hex(),
zap_request_event.content())
for z_tag in zap_request_event.tags():
if z_tag.as_vec()[0] == 'anon':
if len(z_tag.as_vec()) > 1:
print("[" + name + "] Private Zap received.")
decrypted_content = decrypt_private_zap_message(z_tag.as_vec()[1],
keys.secret_key(),
zap_request_event.pubkey())
decrypted_private_event = Event.from_json(decrypted_content)
if decrypted_private_event.kind() == 9733:
sender = decrypted_private_event.pubkey().to_hex()
message = decrypted_private_event.content()
if message != "":
print("Zap Message: " + message)
else:
anon = True
print(
"[" + name + "] Anonymous Zap received. Unlucky, I don't know from whom, and never will")
zap_request_event = Event.from_json(tag.as_vec()[1])
sender = check_for_zapplepay(zap_request_event.pubkey().to_hex(),
zap_request_event.content())
for z_tag in zap_request_event.tags():
if z_tag.as_vec()[0] == 'anon':
if len(z_tag.as_vec()) > 1:
print("[" + name + "] Private Zap received.")
decrypted_content = decrypt_private_zap_message(z_tag.as_vec()[1],
keys.secret_key(),
zap_request_event.pubkey())
decrypted_private_event = Event.from_json(decrypted_content)
if decrypted_private_event.kind() == 9733:
sender = decrypted_private_event.pubkey().to_hex()
message = decrypted_private_event.content()
if message != "":
print("Zap Message: " + message)
else:
anon = True
print(
"[" + name + "] Anonymous Zap received. Unlucky, I don't know from whom, and never will")
return invoice_amount, zapped_event, sender, anon
@ -126,6 +129,26 @@ def check_for_zapplepay(pubkey_hex: str, content: str):
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):
shared_secret = nostr_sdk.generate_shared_key(privkey, pubkey)
if len(shared_secret) != 16 and len(shared_secret) != 32:
@ -150,10 +173,10 @@ def decrypt_private_zap_message(msg: str, privkey: SecretKey, pubkey: PublicKey)
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"):
url = lnurl.decode(lud16)
elif '@' in lud16: #LNaddress
elif '@' in lud16: # LNaddress
url = 'https://' + str(lud16).split('@')[1] + '/.well-known/lnurlp/' + str(lud16).split('@')[0]
else: # No lud16 set or format invalid
return None
@ -164,18 +187,31 @@ def zap(lud16: str, amount: int, content, recipient_pubkey, zapped_event, keys,
encoded_lnurl = lnurl.encode(url)
amount_tag = Tag.parse(['amount', str(amount * 1000)])
relays_tag = Tag.parse(['relays', str(dvm_config.RELAY_LIST)])
p_tag = Tag.parse(['p', recipient_pubkey])
e_tag = Tag.parse(['e', zapped_event])
p_tag = Tag.parse(['p', zapped_event.pubkey().to_hex()])
e_tag = Tag.parse(['e', zapped_event.id().to_hex()])
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,
[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(
zap_request) + "&lnurl=" + encoded_lnurl)
zap_request) + "&lnurl=" + encoded_lnurl)
ob = json.loads(response.content)
return ob["pr"]
except Exception as e:
print(e)
return None
return None