Add onchain logics pt1

This commit is contained in:
Reckless_Satoshi 2022-06-06 10:57:04 -07:00
parent f538d26355
commit 8d0b518222
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
6 changed files with 130 additions and 8 deletions

View File

@ -8,7 +8,7 @@ from base64 import b64decode
from datetime import timedelta, datetime
from django.utils import timezone
from api.models import LNPayment
#######
# Should work with LND (c-lightning in the future if there are features that deserve the work)
@ -176,6 +176,8 @@ class LNNode:
@classmethod
def validate_hold_invoice_locked(cls, lnpayment):
"""Checks if hold invoice is locked"""
from api.models import LNPayment
request = invoicesrpc.LookupInvoiceMsg(
payment_hash=bytes.fromhex(lnpayment.payment_hash))
response = cls.invoicesstub.LookupInvoiceV2(request,
@ -296,7 +298,8 @@ class LNNode:
@classmethod
def pay_invoice(cls, lnpayment):
"""Sends sats. Used for rewards payouts"""
from api.models import LNPayment
fee_limit_sat = int(
max(
lnpayment.num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),

View File

@ -17,8 +17,11 @@ from decouple import config
from pathlib import Path
import json
from control.models import BalanceLog
MIN_TRADE = int(config("MIN_TRADE"))
MAX_TRADE = int(config("MAX_TRADE"))
MIN_SWAP_AMOUNT = int(config("MIN_SWAP_AMOUNT"))
FEE = float(config("FEE"))
DEFAULT_BOND_SIZE = float(config("DEFAULT_BOND_SIZE"))
@ -118,7 +121,7 @@ class LNPayment(models.Model):
blank=True)
num_satoshis = models.PositiveBigIntegerField(validators=[
MinValueValidator(100),
MaxValueValidator(MAX_TRADE * (1 + DEFAULT_BOND_SIZE + FEE)),
MaxValueValidator(1.5 * MAX_TRADE),
])
# Fee in sats with mSats decimals fee_msat
fee = models.DecimalField(max_digits=10, decimal_places=3, default=0, null=False, blank=False)
@ -163,6 +166,96 @@ class LNPayment(models.Model):
# We created a truncated property for display 'hash'
return truncatechars(self.payment_hash, 10)
class OnchainPayment(models.Model):
class Concepts(models.IntegerChoices):
PAYBUYER = 3, "Payment to buyer"
class Status(models.IntegerChoices):
CREAT = 0, "Created" # User was given platform fees and suggested mining fees
VALID = 1, "Valid" # Valid onchain address submitted
MEMPO = 2, "In mempool" # Tx is sent to mempool
CONFI = 3, "Confirmed" # Tx is confirme +2 blocks
# payment use details
concept = models.PositiveSmallIntegerField(choices=Concepts.choices,
null=False,
default=Concepts.PAYBUYER)
status = models.PositiveSmallIntegerField(choices=Status.choices,
null=False,
default=Status.VALID)
# payment info
address = models.CharField(max_length=100,
unique=False,
default=None,
null=True,
blank=True)
txid = models.CharField(max_length=64,
unique=True,
null=True,
default=None,
blank=True)
num_satoshis = models.PositiveBigIntegerField(validators=[
MinValueValidator(0.7 * MIN_SWAP_AMOUNT),
MaxValueValidator(1.5 * MAX_TRADE),
])
# fee in sats/vbyte with mSats decimals fee_msat
suggested_mining_fee_rate = models.DecimalField(max_digits=6,
decimal_places=3,
default=1.05,
null=False,
blank=False)
mining_fee_rate = models.DecimalField(max_digits=6,
decimal_places=3,
default=1.05,
null=False,
blank=False)
mining_fee_sats = models.PositiveBigIntegerField(default=0,
null=False,
blank=False)
# platform onchain/channels balance at creattion, swap fee rate as percent of total volume
node_balance = models.ForeignKey(BalanceLog,
related_name="balance",
on_delete=models.SET_NULL,
null=True)
swap_fee_rate = models.DecimalField(max_digits=4,
decimal_places=2,
default=2,
null=False,
blank=False)
created_at = models.DateTimeField(default=timezone.now)
# involved parties
receiver = models.ForeignKey(User,
related_name="tx_receiver",
on_delete=models.SET_NULL,
null=True,
default=None)
def __str__(self):
if self.txid:
txname = str(self.txid)[:8]
else:
txname = str(self.id)
return f"TX-{txname}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}"
class Meta:
verbose_name = "Lightning payment"
verbose_name_plural = "Lightning payments"
@property
def hash(self):
# Payment hash is the primary key of LNpayments
# However it is too long for the admin panel.
# We created a truncated property for display 'hash'
return truncatechars(self.payment_hash, 10)
class Order(models.Model):

View File

@ -79,7 +79,7 @@ def follow_send_payment(hash):
float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
float(config("MIN_FLAT_ROUTING_FEE_LIMIT")),
)) # 1000 ppm or 10 sats
timeout_seconds = int(config("REWARRDS_TIMEOUT_SECONDS"))
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
request = LNNode.routerrpc.SendPaymentRequest(
payment_request=lnpayment.invoice,

View File

@ -1,8 +1,7 @@
import requests, ring, os
from decouple import config
import numpy as np
import requests
import coinaddrvalidator as addr
from api.models import Order
def get_tor_session():
@ -12,6 +11,24 @@ def get_tor_session():
'https': 'socks5://127.0.0.1:9050'}
return session
def validate_onchain_address(address):
'''
Validates an onchain address
'''
validation = addr.validate('btc', address.encode('utf-8'))
if not validation.valid:
return False
NETWORK = str(config('NETWORK'))
if NETWORK == 'mainnet':
if validation.network == 'main':
return True
elif NETWORK == 'testnet':
if validation.network == 'test':
return True
market_cache = {}
@ring.dict(market_cache, expire=3) # keeps in cache for 3 seconds
def get_exchange_rates(currencies):

View File

@ -416,9 +416,10 @@ class OrderView(viewsets.ViewSet):
order = Order.objects.get(id=order_id)
# action is either 1)'take', 2)'confirm', 3)'cancel', 4)'dispute' , 5)'update_invoice'
# 6)'submit_statement' (in dispute), 7)'rate_user' , 'rate_platform'
# 5.b)'update_address' 6)'submit_statement' (in dispute), 7)'rate_user' , 8)'rate_platform'
action = serializer.data.get("action")
invoice = serializer.data.get("invoice")
address = serializer.data.get("address")
statement = serializer.data.get("statement")
rating = serializer.data.get("rating")
@ -464,6 +465,13 @@ class OrderView(viewsets.ViewSet):
invoice)
if not valid:
return Response(context, status.HTTP_400_BAD_REQUEST)
# 2.b) If action is 'update invoice'
if action == "update_address":
valid, context = Logics.update_address(order, request.user,
address)
if not valid:
return Response(context, status.HTTP_400_BAD_REQUEST)
# 3) If action is cancel
elif action == "cancel":

View File

@ -25,4 +25,5 @@ psycopg2==2.9.3
SQLAlchemy==1.4.31
django-import-export==2.7.1
requests[socks]
python-gnupg==0.4.9
python-gnupg==0.4.9
coinaddrvalidator==1.1.3