Add automatic dispute resolution (#437)

* Add automatic dispute resolution logic

* Small fixes
This commit is contained in:
Reckless_Satoshi 2023-04-24 11:05:52 +00:00 committed by GitHub
parent b399d6a608
commit 7e47fb60bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 7 deletions

View File

@ -11,6 +11,7 @@ from api.lightning.node import LNNode
from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order, User
from api.tasks import send_notification
from api.utils import validate_onchain_address
from chat.models import Message
FEE = float(config("FEE"))
MAKER_FEE_SPLIT = float(config("MAKER_FEE_SPLIT"))
@ -428,6 +429,65 @@ class Logics:
cls.publish_order(order)
return True
@classmethod
def automatic_dispute_resolution(cls, order):
"""Simple case where a dispute can be solved with a
priori knowledge. For example, a dispute that opens
at expiration on an order where one of the participants
never sent a message on the chat and never marked 'fiat
sent'. By solving the dispute automatically before
flagging it as dispute, we avoid having to settle the
bonds"""
# If fiat has been marked as sent, automatic dispute
# resolution is not possible.
if order.is_fiat_sent:
return False
# If the order has not entered dispute due to time expire
# (a user triggered it), automatic dispute resolution is
# not possible.
if order.expires_at >= timezone.now():
return False
num_messages_taker = len(
Message.objects.filter(order=order, sender=order.taker)
)
num_messages_maker = len(
Message.objects.filter(order=order, sender=order.maker)
)
if num_messages_maker == num_messages_taker == 0:
cls.return_escrow(order)
cls.settle_bond(order.maker_bond)
cls.settle_bond(order.taker_bond)
order.status = Order.Status.DIS
elif num_messages_maker == 0:
cls.return_escrow(order)
cls.settle_bond(order.maker_bond)
cls.return_bond(order.taker_bond)
cls.add_slashed_rewards(order.maker_bond, order.taker.profile)
order.status = Order.Status.MLD
elif num_messages_maker == 0:
cls.return_escrow(order)
cls.settle_bond(order.maker_bond)
cls.return_bond(order.taker_bond)
cls.add_slashed_rewards(order.taker_bond, order.maker.profile)
order.status = Order.Status.TLD
else:
return False
order.is_disputed = True
order.expires_at = timezone.now() + timedelta(
seconds=order.t_to_expire(Order.Status.DIS)
)
order.save()
send_notification.delay(order_id=order.id, message="dispute_opened")
return True
@classmethod
def open_dispute(cls, order, user=None):
@ -446,6 +506,11 @@ class Logics:
"bad_request": "You cannot open a dispute of this order at this stage"
}
automatically_solved = cls.automatic_dispute_resolution(order)
if automatically_solved:
return True, None
if not order.trade_escrow.status == LNPayment.Status.SETLED:
cls.settle_escrow(order)
cls.settle_bond(order.maker_bond)
@ -481,9 +546,9 @@ class Logics:
"bad_request": "Only orders in dispute accept dispute statements"
}
if len(statement) > 10000:
if len(statement) > 50000:
return False, {
"bad_statement": "The statement is longer than 10000 characters"
"bad_statement": "The statement and chatlogs are longer than 50000 characters"
}
if len(statement) < 100:
@ -1601,7 +1666,6 @@ class Logics:
def add_slashed_rewards(cls, bond, profile):
"""
When a bond is slashed due to overtime, rewards the user that was waiting.
If participants of the order were referred, the reward is given to the referees.
"""
reward_fraction = float(config("SLASHED_BOND_REWARD_SPLIT"))
reward = int(bond.num_satoshis * reward_fraction)

View File

@ -444,10 +444,10 @@ class Order(models.Model):
# in dispute
is_disputed = models.BooleanField(default=False, null=False)
maker_statement = models.TextField(
max_length=10000, null=True, default=None, blank=True
max_length=50000, null=True, default=None, blank=True
)
taker_statement = models.TextField(
max_length=10000, null=True, default=None, blank=True
max_length=50000, null=True, default=None, blank=True
)
# LNpayments

View File

@ -492,7 +492,7 @@ class UpdateOrderSerializer(serializers.Serializer):
routing_budget_ppm = serializers.IntegerField(
default=0,
min_value=0,
max_value=100000,
max_value=100001,
allow_null=True,
required=False,
help_text="Max budget to allocate for routing in PPM",
@ -501,7 +501,7 @@ class UpdateOrderSerializer(serializers.Serializer):
max_length=100, allow_null=True, allow_blank=True, default=None
)
statement = serializers.CharField(
max_length=11000, allow_null=True, allow_blank=True, default=None
max_length=500000, allow_null=True, allow_blank=True, default=None
)
action = serializers.ChoiceField(
choices=(