mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Add fiat sent/received confirmation tests. Improve API documentation
This commit is contained in:
parent
3e0d451e97
commit
5674337b32
@ -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(
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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"])
|
||||
|
Loading…
Reference in New Issue
Block a user