Add fiat sent/received confirmation tests. Improve API documentation

This commit is contained in:
Reckless_Satoshi 2023-11-16 13:28:53 +00:00 committed by Reckless_Satoshi
parent 3e0d451e97
commit 5674337b32
7 changed files with 193 additions and 33 deletions

View File

@ -1915,7 +1915,9 @@ class Logics:
else:
summary["received_sats"] = order.payout.num_satoshis
summary["payment_hash"] = order.payout.payment_hash
summary["preimage"] = order.payout.preimage
summary["preimage"] = (
order.payout.preimage if order.payout.preimage else "processing"
)
summary["trade_fee_sats"] = round(
order.last_satoshis
- summary["received_sats"]
@ -1959,7 +1961,7 @@ class Logics:
order.save(update_fields=["contract_finalization_time"])
platform_summary["contract_total_time"] = (
order.contract_finalization_time - order.last_satoshis_time
)
).total_seconds()
if not order.is_swap:
platform_summary["routing_budget_sats"] = order.payout.routing_budget_sats
platform_summary["trade_revenue_sats"] = int(

View File

@ -160,7 +160,6 @@ class NickGenerator:
attempts = []
for i in range(num_runs):
string = str(random.uniform(0, 1_000_000))
hash = hashlib.sha256(str.encode(string)).hexdigest()
@ -179,7 +178,6 @@ class NickGenerator:
if __name__ == "__main__":
# Just for code timming
t0 = time.time()

View File

@ -84,15 +84,24 @@ class ListOrderSerializer(serializers.ModelSerializer):
# Only used in oas_schemas
class SummarySerializer(serializers.Serializer):
sent_fiat = serializers.IntegerField(
sent_fiat = serializers.FloatField(
required=False, help_text="same as `amount` (only for buyer)"
)
received_fiat = serializers.FloatField(
required=False, help_text="same as `amount` (only for seller)"
)
sent_sats = serializers.IntegerField(
required=False, help_text="The total sats you sent (only for seller)"
)
received_sats = serializers.IntegerField(
required=False, help_text="same as `trade_satoshis` (only for buyer)"
)
is_swap = serializers.BooleanField(
required=False, help_text="True if the payout was on-chain (only for buyer)"
)
is_buyer = serializers.BooleanField(
required=False, help_text="True if the robot is the order buyer"
)
received_onchain_sats = serializers.IntegerField(
required=False,
help_text="The on-chain sats received (only for buyer and if `is_swap` is `true`)",
@ -109,15 +118,26 @@ class SummarySerializer(serializers.Serializer):
required=False,
help_text="same as `swap_fee_rate` (only for buyer and if `is_swap` is `true`",
)
sent_sats = serializers.IntegerField(
required=False, help_text="The total sats you sent (only for seller)"
bond_size_sats = serializers.IntegerField(
required=False, help_text="The amount of Satoshis at stake"
)
received_fiat = serializers.IntegerField(
required=False, help_text="same as `amount` (only for seller)"
bond_size_percent = serializers.FloatField(
required=False, help_text="The relative size of Satoshis at stake"
)
trade_fee_sats = serializers.IntegerField(
required=False,
help_text="Exchange fees in sats (Does not include swap fee and miner fee)",
help_text="Exchange fees in sats (does not include swap fee and miner fee)",
)
trade_fee_percent = serializers.FloatField(
required=False,
help_text="Exchange fees in percent (does not include swap fee and miner fee)",
)
payment_hash = serializers.CharField(
required=False, help_text="The payment_hash of the payout invoice"
)
preimage = serializers.CharField(
required=False,
help_text="The preimage of the payout invoice (proof of payment)",
)
@ -138,6 +158,13 @@ class PlatformSummarySerializer(serializers.Serializer):
trade_revenue_sats = serializers.IntegerField(
required=False, help_text="The sats the exchange earned from the trade"
)
routing_budget_sats = serializers.FloatField(
required=False, help_text="The budget allocated for routing costs in Satoshis"
)
contract_exchange_rate = serializers.FloatField(
required=False,
help_text="The exchange rate applied to this contract. Taken from externals APIs exactly when the taker bond was locked.",
)
# Only used in oas_schemas

View File

@ -449,7 +449,7 @@ class OrderView(viewsets.ViewSet):
Order.Status.FAI,
]:
data["public_duration"] = order.public_duration
data["bond_size"] = order.bond_size
data["bond_size"] = str(order.bond_size)
# Adds trade summary
if order.status in [Order.Status.SUC, Order.Status.PAY, Order.Status.FAI]:

View File

@ -1788,6 +1788,15 @@ components:
trade_revenue_sats:
type: integer
description: The sats the exchange earned from the trade
routing_budget_sats:
type: number
format: double
description: The budget allocated for routing costs in Satoshis
contract_exchange_rate:
type: number
format: double
description: The exchange rate applied to this contract. Taken from externals
APIs exactly when the taker bond was locked.
PostMessage:
type: object
properties:
@ -1873,14 +1882,25 @@ components:
type: object
properties:
sent_fiat:
type: integer
type: number
format: double
description: same as `amount` (only for buyer)
received_fiat:
type: number
format: double
description: same as `amount` (only for seller)
sent_sats:
type: integer
description: The total sats you sent (only for seller)
received_sats:
type: integer
description: same as `trade_satoshis` (only for buyer)
is_swap:
type: boolean
description: True if the payout was on-chain (only for buyer)
is_buyer:
type: boolean
description: True if the robot is the order buyer
received_onchain_sats:
type: integer
description: The on-chain sats received (only for buyer and if `is_swap`
@ -1898,16 +1918,28 @@ components:
format: double
description: same as `swap_fee_rate` (only for buyer and if `is_swap` is
`true`
sent_sats:
bond_size_sats:
type: integer
description: The total sats you sent (only for seller)
received_fiat:
type: integer
description: same as `amount` (only for seller)
description: The amount of Satoshis at stake
bond_size_percent:
type: number
format: double
description: The relative size of Satoshis at stake
trade_fee_sats:
type: integer
description: Exchange fees in sats (Does not include swap fee and miner
description: Exchange fees in sats (does not include swap fee and miner
fee)
trade_fee_percent:
type: number
format: double
description: Exchange fees in percent (does not include swap fee and miner
fee)
payment_hash:
type: string
description: The payment_hash of the payout invoice
preimage:
type: string
description: The preimage of the payout invoice (proof of payment)
Tick:
type: object
properties:

View File

@ -58,7 +58,7 @@ def wait_for_lnd_node_sync(node_name):
return
else:
sys.stdout.write(
f"\rWaiting for {node_name} node chain sync {round(waited,1)}s"
f"\rWaiting for {node_name} node chain sync {round(waited, 1)}s"
)
sys.stdout.flush()
waited += WAIT_STEP
@ -88,14 +88,14 @@ def wait_for_active_channels(lnvendor, node_name="coordinator"):
return
else:
sys.stdout.write(
f"\rWaiting for {node_name} LND node channel to be active {round(waited,1)}s"
f"\rWaiting for {node_name} LND node channel to be active {round(waited, 1)}s"
)
elif lnvendor == "CLN":
if CLN_has_active_channels():
return
else:
sys.stdout.write(
f"\rWaiting for {node_name} CLN node channel to be active {round(waited,1)}s"
f"\rWaiting for {node_name} CLN node channel to be active {round(waited, 1)}s"
)
sys.stdout.flush()
@ -111,7 +111,7 @@ def wait_for_cln_node_sync():
response = CLNNode.get_info()
if response.warning_bitcoind_sync or response.warning_lightningd_sync:
sys.stdout.write(
f"\rWaiting for coordinator CLN node sync {round(waited,1)}s"
f"\rWaiting for coordinator CLN node sync {round(waited, 1)}s"
)
sys.stdout.flush()
waited += WAIT_STEP
@ -130,7 +130,7 @@ def wait_for_cln_active_channels():
return
else:
sys.stdout.write(
f"\rWaiting for coordinator CLN node channels to be active {round(waited,1)}s"
f"\rWaiting for coordinator CLN node channels to be active {round(waited, 1)}s"
)
sys.stdout.flush()
waited += WAIT_STEP

View File

@ -1,4 +1,5 @@
import json
import random
from datetime import datetime
from decimal import Decimal
@ -583,8 +584,11 @@ class TradeTest(BaseAPITestCase):
passphrase_path=f"tests/robots/{robot_index}/token",
private_key_path=f"tests/robots/{robot_index}/enc_priv_key",
)
body = {"action": "update_address", "address": signed_payout_address}
body = {
"action": "update_address",
"address": signed_payout_address,
"mining_fee_rate": 50,
}
response = self.client.post(path + params, body, **headers)
return response
@ -602,15 +606,18 @@ class TradeTest(BaseAPITestCase):
def test_trade_to_submitted_address(self):
"""
Tests a trade from order creation until escrow locked, before
invoice/address is submitted by buyer.
Tests a trade from order creation until escrow locked and
address is submitted by buyer.
"""
maker_index = 1
taker_index = 2
maker_form = self.maker_form_buy_with_range
take_amount = round(
random.uniform(maker_form["min_amount"], maker_form["max_amount"]), 2
)
response = self.trade_to_submitted_address(
maker_form, 80, maker_index, taker_index
maker_form, take_amount, maker_index, taker_index
)
data = response.json()
@ -624,7 +631,9 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
self.cancel_order(data["id"])
def submit_payout_invoice(self, order_id, num_satoshis, robot_index=1):
def submit_payout_invoice(
self, order_id, num_satoshis, routing_budget, robot_index=1
):
path = reverse("order")
params = f"?order_id={order_id}"
headers = self.get_robot_auth(robot_index)
@ -635,7 +644,11 @@ class TradeTest(BaseAPITestCase):
passphrase_path=f"tests/robots/{robot_index}/token",
private_key_path=f"tests/robots/{robot_index}/enc_priv_key",
)
body = {"action": "update_invoice", "invoice": signed_payout_invoice}
body = {
"action": "update_invoice",
"invoice": signed_payout_invoice,
"routing_budget_ppm": routing_budget,
}
response = self.client.post(path + params, body, **headers)
@ -653,21 +666,25 @@ class TradeTest(BaseAPITestCase):
response = self.submit_payout_invoice(
response_escrow_locked.json()["id"],
response_get.json()["trade_satoshis"],
0,
maker_index,
)
return response
def test_trade_to_submitted_invoice(self):
"""
Tests a trade from order creation until escrow locked, before
invoice/address is submitted by buyer.
Tests a trade from order creation until escrow locked and
invoice is submitted by buyer.
"""
maker_index = 1
taker_index = 2
maker_form = self.maker_form_buy_with_range
take_amount = round(
random.uniform(maker_form["min_amount"], maker_form["max_amount"]), 2
)
response = self.trade_to_submitted_invoice(
maker_form, 80, maker_index, taker_index
maker_form, take_amount, maker_index, taker_index
)
data = response.json()
@ -679,3 +696,87 @@ class TradeTest(BaseAPITestCase):
# Cancel order to avoid leaving pending HTLCs after a successful test
self.cancel_order(data["id"])
def confirm_fiat(self, order_id, robot_index=1):
path = reverse("order")
params = f"?order_id={order_id}"
headers = self.get_robot_auth(robot_index)
body = {"action": "confirm"}
response = self.client.post(path + params, body, **headers)
return response
def trade_to_confirm_fiat_sent_LN(
self, maker_form, take_amount=80, maker_index=1, taker_index=2
):
response_submitted_invoice = self.trade_to_submitted_invoice(
maker_form, take_amount, maker_index, taker_index
)
response = self.confirm_fiat(
response_submitted_invoice.json()["id"], maker_index
)
return response
def test_trade_to_confirm_fiat_sent_LN(self):
"""
Tests a trade from order creation until fiat sent confirmed
"""
maker_index = 1
taker_index = 2
maker_form = self.maker_form_buy_with_range
take_amount = round(
random.uniform(maker_form["min_amount"], maker_form["max_amount"]), 2
)
response = self.trade_to_confirm_fiat_sent_LN(
maker_form, take_amount, maker_index, taker_index
)
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(data["status_message"], Order.Status(Order.Status.FSE).label)
self.assertTrue(data["is_fiat_sent"])
# Cancel order to avoid leaving pending HTLCs after a successful test
self.cancel_order(data["id"], maker_index)
self.cancel_order(data["id"], taker_index)
def trade_to_confirm_fiat_received_LN(
self, maker_form, take_amount=80, maker_index=1, taker_index=2
):
response_submitted_invoice = self.trade_to_confirm_fiat_sent_LN(
maker_form, take_amount, maker_index, taker_index
)
response = self.confirm_fiat(
response_submitted_invoice.json()["id"], taker_index
)
return response
def test_trade_to_confirm_fiat_received_LN(self):
"""
Tests a trade from order creation until fiat received is confirmed by seller/taker
"""
maker_index = 1
taker_index = 2
maker_form = self.maker_form_buy_with_range
take_amount = round(
random.uniform(maker_form["min_amount"], maker_form["max_amount"]), 2
)
response = self.trade_to_confirm_fiat_received_LN(
maker_form, take_amount, maker_index, taker_index
)
data = response.json()
self.assertEqual(response.status_code, 200)
self.assertResponse(response)
self.assertEqual(data["status_message"], Order.Status(Order.Status.PAY).label)
self.assertTrue(data["is_fiat_sent"])
self.assertFalse(data["is_disputed"])
self.assertFalse(data["maker_locked"])
self.assertFalse(data["taker_locked"])
self.assertFalse(data["escrow_locked"])