mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-13 19:06:26 +00:00
137 lines
4.8 KiB
Python
137 lines
4.8 KiB
Python
import grpc, os, hashlib, secrets, json
|
|
import lightning_pb2 as lnrpc, lightning_pb2_grpc as lightningstub
|
|
import invoices_pb2 as invoicesrpc, invoices_pb2_grpc as invoicesstub
|
|
|
|
from decouple import config
|
|
from base64 import b64decode
|
|
|
|
from datetime import timedelta, datetime
|
|
from django.utils import timezone
|
|
|
|
#######
|
|
# Should work with LND (c-lightning in the future if there are features that deserve the work)
|
|
#######
|
|
|
|
CERT = b64decode(config('LND_CERT_BASE64'))
|
|
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
|
|
LND_GRPC_HOST = config('LND_GRPC_HOST')
|
|
|
|
class LNNode():
|
|
|
|
os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'
|
|
|
|
creds = grpc.ssl_channel_credentials(CERT)
|
|
channel = grpc.secure_channel(LND_GRPC_HOST, creds)
|
|
|
|
lightningstub = lightningstub.LightningStub(channel)
|
|
invoicesstub = invoicesstub.InvoicesStub(channel)
|
|
|
|
def decode_payreq(invoice):
|
|
'''Decodes a lightning payment request (invoice)'''
|
|
request = lnrpc.PayReqString(pay_req=invoice)
|
|
response = lightningstub.DecodePayReq(request, metadata=[('macaroon', MACAROON.hex())])
|
|
return response
|
|
|
|
def cancel_return_hold_invoice(payment_hash):
|
|
'''Cancels or returns a hold invoice'''
|
|
request = invoicesrpc.CancelInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
|
|
response = invoicesstub.CancelInvoice(request, metadata=[('macaroon', MACAROON.hex())])
|
|
|
|
# Fix this: tricky because canceling sucessfully an invoice has no response. TODO
|
|
if response == None:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def settle_hold_invoice(preimage):
|
|
# SETTLING A HODL INVOICE
|
|
request = invoicesrpc.SettleInvoiceMsg(preimage=preimage)
|
|
response = invoicesstub.SettleInvoice(request, metadata=[('macaroon', MACAROON.hex())])
|
|
# Fix this: tricky because canceling sucessfully an invoice has no response. TODO
|
|
if response == None:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
@classmethod
|
|
def gen_hold_invoice(cls, num_satoshis, description, expiry):
|
|
'''Generates hold invoice'''
|
|
|
|
# The preimage will be a random hash of 256 bits entropy
|
|
preimage = hashlib.sha256(secrets.token_bytes(nbytes=32)).digest()
|
|
|
|
# Its hash is used to generate the hold invoice
|
|
preimage_hash = hashlib.sha256(preimage).digest()
|
|
|
|
request = invoicesrpc.AddHoldInvoiceRequest(
|
|
memo=description,
|
|
value=num_satoshis,
|
|
hash=preimage_hash,
|
|
expiry=expiry)
|
|
response = invoicesstub.AddHoldInvoice(request, metadata=[('macaroon', MACAROON.hex())])
|
|
|
|
invoice = response.payment_request
|
|
|
|
payreq_decoded = cls.decode_payreq(invoice)
|
|
payment_hash = payreq_decoded.payment_hash
|
|
created_at = timezone.make_aware(datetime.fromtimestamp(payreq_decoded.timestamp))
|
|
expires_at = created_at + timedelta(seconds=payreq_decoded.expiry)
|
|
|
|
return invoice, preimage, payment_hash, created_at, expires_at
|
|
|
|
def validate_hold_invoice_locked(payment_hash):
|
|
'''Checks if hodl invoice is locked'''
|
|
|
|
# request = ln.InvoiceSubscription()
|
|
# When invoice is settled, return true. If time expires, return False.
|
|
# for invoice in stub.SubscribeInvoices(request):
|
|
# print(invoice)
|
|
|
|
return True
|
|
|
|
@classmethod
|
|
def validate_ln_invoice(cls, invoice, num_satoshis):
|
|
'''Checks if the submited LN invoice comforms to expectations'''
|
|
|
|
try:
|
|
payreq_decoded = cls.decode_payreq(invoice)
|
|
except:
|
|
return False, {'bad_invoice':'Does not look like a valid lightning invoice'}
|
|
|
|
if not payreq_decoded.num_satoshis == num_satoshis:
|
|
context = {'bad_invoice':f'The invoice provided is not for {num_satoshis}'}
|
|
return False, context, None, None, None, None
|
|
|
|
created_at = timezone.make_aware(datetime.fromtimestamp(payreq_decoded.timestamp))
|
|
expires_at = created_at + timedelta(seconds=payreq_decoded.expiry)
|
|
|
|
if expires_at < timezone.now():
|
|
context = {'bad_invoice':f'The invoice provided has already expired'}
|
|
return False, context, None, None, None, None
|
|
|
|
description = payreq_decoded.expiry.description
|
|
payment_hash = payreq_decoded.payment_hash
|
|
|
|
return True, None, description, payment_hash, created_at, expires_at
|
|
|
|
def pay_invoice(invoice):
|
|
'''Sends sats to buyer, or cancelinvoices'''
|
|
return True
|
|
|
|
def check_if_hold_invoice_is_locked(payment_hash):
|
|
'''Every hodl invoice that is in state INVGEN
|
|
Has to be checked for payment received until
|
|
the window expires'''
|
|
|
|
return True
|
|
|
|
def double_check_htlc_is_settled(payment_hash):
|
|
''' Just as it sounds. Better safe than sorry!'''
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|