Skip to content

Commit d002098

Browse files
Update LNpayment model and logics to use user's routing budget
1 parent 917d0a7 commit d002098

File tree

12 files changed

+68
-36
lines changed

12 files changed

+68
-36
lines changed

api/lightning/node.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ def resetmc(cls):
258258
return True
259259

260260
@classmethod
261-
def validate_ln_invoice(cls, invoice, num_satoshis):
261+
def validate_ln_invoice(cls, invoice, num_satoshis, routing_budget_ppm):
262262
"""Checks if the submited LN invoice comforms to expectations"""
263263

264264
payout = {
@@ -283,9 +283,8 @@ def validate_ln_invoice(cls, invoice, num_satoshis):
283283
route_hints = payreq_decoded.route_hints
284284

285285
# Max amount RoboSats will pay for routing
286-
max_routing_fee_sats = max(
287-
num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
288-
float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")),
286+
max_routing_fee_sats = int(
287+
float(num_satoshis) * float(routing_budget_ppm) / 1000000
289288
)
290289

291290
if route_hints:
@@ -306,7 +305,7 @@ def validate_ln_invoice(cls, invoice, num_satoshis):
306305
# If the cheapest possible private route is more expensive than what RoboSats is willing to pay
307306
if min(routes_cost) >= max_routing_fee_sats:
308307
payout["context"] = {
309-
"bad_invoice": "The invoice submitted only has a trick on the routing hints, you might be using an incompatible wallet (probably Muun? Use an onchain address instead!). Check the wallet compatibility guide at wallets.robosats.com"
308+
"bad_invoice": "The invoice hinted private routes are not payable within the submitted routing budget."
310309
}
311310
return payout
312311

api/logics.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def update_address(cls, order, user, address, mining_fee_rate):
721721
return True, None
722722

723723
@classmethod
724-
def update_invoice(cls, order, user, invoice):
724+
def update_invoice(cls, order, user, invoice, routing_budget_ppm):
725725

726726
# Empty invoice?
727727
if not invoice:
@@ -754,7 +754,11 @@ def update_invoice(cls, order, user, invoice):
754754
cls.cancel_onchain_payment(order)
755755

756756
num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"]
757-
payout = LNNode.validate_ln_invoice(invoice, num_satoshis)
757+
routing_budget_sats = float(num_satoshis) * (
758+
float(routing_budget_ppm) / 1000000
759+
)
760+
num_satoshis = int(num_satoshis - routing_budget_sats)
761+
payout = LNNode.validate_ln_invoice(invoice, num_satoshis, routing_budget_ppm)
758762

759763
if not payout["valid"]:
760764
return False, payout["context"]
@@ -765,6 +769,8 @@ def update_invoice(cls, order, user, invoice):
765769
sender=User.objects.get(username=ESCROW_USERNAME),
766770
order_paid_LN=order, # In case this user has other payouts, update the one related to this order.
767771
receiver=user,
772+
routing_budget_ppm=routing_budget_ppm,
773+
routing_budget_sats=routing_budget_sats,
768774
# if there is a LNPayment matching these above, it updates that one with defaults below.
769775
defaults={
770776
"invoice": invoice,
@@ -1679,7 +1685,9 @@ def summarize_trade(cls, order, user):
16791685
else:
16801686
summary["received_sats"] = order.payout.num_satoshis
16811687
summary["trade_fee_sats"] = round(
1682-
order.last_satoshis - summary["received_sats"]
1688+
order.last_satoshis
1689+
- summary["received_sats"]
1690+
- order.payout.routing_budget_sats
16831691
)
16841692
# Only add context for swap costs if the user is the swap recipient. Peer should not know whether it was a swap
16851693
if users[order_user] == user and order.is_swap:
@@ -1716,11 +1724,11 @@ def summarize_trade(cls, order, user):
17161724
order.contract_finalization_time - order.last_satoshis_time
17171725
)
17181726
if not order.is_swap:
1719-
platform_summary["routing_fee_sats"] = order.payout.fee
1727+
platform_summary["routing_budget_sats"] = order.payout.routing_budget_sats
17201728
platform_summary["trade_revenue_sats"] = int(
17211729
order.trade_escrow.num_satoshis
17221730
- order.payout.num_satoshis
1723-
- order.payout.fee
1731+
- order.payout.routing_budget_sats
17241732
)
17251733
else:
17261734
platform_summary["routing_fee_sats"] = 0

api/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ class FailureReason(models.IntegerChoices):
126126
MaxValueValidator(1.5 * MAX_TRADE),
127127
]
128128
)
129+
# Routing budget in PPM
130+
routing_budget = models.PositiveBigIntegerField(
131+
default=0,
132+
null=False,
133+
validators=[
134+
MinValueValidator(0),
135+
MaxValueValidator(100000),
136+
],
137+
)
138+
# Routing budget in Sats. Only for reporting summaries.
139+
routing_budget_sats = models.DecimalField(
140+
max_digits=10, decimal_places=3, default=0, null=False, blank=False
141+
)
129142
# Fee in sats with mSats decimals fee_msat
130143
fee = models.DecimalField(
131144
max_digits=10, decimal_places=3, default=0, null=False, blank=False

api/serializers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,14 @@ class UpdateOrderSerializer(serializers.Serializer):
489489
invoice = serializers.CharField(
490490
max_length=2000, allow_null=True, allow_blank=True, default=None
491491
)
492+
routing_budget_ppm = serializers.IntegerField(
493+
default=0,
494+
min_value=0,
495+
max_value=100000,
496+
allow_null=True,
497+
required=False,
498+
help_text="Max budget to allocate for routing in PPM",
499+
)
492500
address = serializers.CharField(
493501
max_length=100, allow_null=True, allow_blank=True, default=None
494502
)

api/tasks.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,10 @@ def follow_send_payment(hash):
8686
from api.models import LNPayment, Order
8787

8888
lnpayment = LNPayment.objects.get(payment_hash=hash)
89+
# Defaults is 0ppm. Set by the user over API. Defaults to 1000 ppm on ReactJS frontend.
8990
fee_limit_sat = int(
90-
max(
91-
lnpayment.num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
92-
float(config("MIN_FLAT_ROUTING_FEE_LIMIT")),
93-
)
94-
) # 1000 ppm or 10 sats
91+
float(lnpayment.num_satoshis) * float(lnpayment.routing_budget_ppm) / 1000000
92+
)
9593
timeout_seconds = int(config("PAYOUT_TIMEOUT_SECONDS"))
9694

9795
request = LNNode.routerrpc.SendPaymentRequest(
@@ -145,7 +143,6 @@ def follow_send_payment(hash):
145143
],
146144
"IN_FLIGHT": False,
147145
}
148-
print(context)
149146

150147
# If failed due to not route, reset mission control. (This won't scale well, just a temporary fix)
151148
# ResetMC deactivate temporary for tests

api/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ def take_update_confirm_dispute_cancel(self, request, format=None):
501501
# 5.b)'update_address' 6)'submit_statement' (in dispute), 7)'rate_user' , 8)'rate_platform'
502502
action = serializer.data.get("action")
503503
invoice = serializer.data.get("invoice")
504+
routing_budget_ppm = serializer.data.get("routing_budget_ppm", 0)
504505
address = serializer.data.get("address")
505506
mining_fee_rate = serializer.data.get("mining_fee_rate")
506507
statement = serializer.data.get("statement")
@@ -543,7 +544,9 @@ def take_update_confirm_dispute_cancel(self, request, format=None):
543544

544545
# 2) If action is 'update invoice'
545546
elif action == "update_invoice":
546-
valid, context = Logics.update_invoice(order, request.user, invoice)
547+
valid, context = Logics.update_invoice(
548+
order, request.user, invoice, routing_budget_ppm
549+
)
547550
if not valid:
548551
return Response(context, status.HTTP_400_BAD_REQUEST)
549552

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ services:
5353
environment:
5454
TOR_PROXY_IP: 127.0.0.1
5555
TOR_PROXY_PORT: 9050
56-
ROBOSATS_ONION: robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
56+
ROBOSATS_ONION: robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion
5757
network_mode: service:tor
5858
volumes:
5959
- ./frontend/static:/usr/src/robosats/static

frontend/src/components/Notifications/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState } from 'react';
2-
import { StringIfPlural, useTranslation } from 'react-i18next';
2+
import { useTranslation } from 'react-i18next';
33
import {
44
Tooltip,
55
Alert,
@@ -262,7 +262,6 @@ const Notifications = ({
262262
} else if (order?.is_seller && status > 7 && oldStatus < 7) {
263263
message = Messages.escrowLocked;
264264
} else if ([9, 10].includes(status) && oldStatus < 9) {
265-
console.log('yoooo');
266265
message = Messages.chat;
267266
} else if (order?.is_seller && [13, 14, 15].includes(status) && oldStatus < 13) {
268267
message = Messages.successful;
@@ -333,7 +332,6 @@ const Notifications = ({
333332
return (
334333
<StyledTooltip
335334
open={show}
336-
style={{ padding: 0, backgroundColor: 'black' }}
337335
placement={windowWidth > 60 ? 'left' : 'bottom'}
338336
title={
339337
<Alert

frontend/src/components/TradeBox/Forms/LightningPayout.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,13 @@ export const LightningPayoutForm = ({
8989
const [loadingLnproxy, setLoadingLnproxy] = useState<boolean>(false);
9090

9191
const computeInvoiceAmount = function () {
92-
//const tradeAmount = order.trade_satoshis
93-
const tradeAmount = 1000000;
94-
return tradeAmount - Math.floor((tradeAmount / 1000000) * lightning.routingBudgetPPM);
92+
const tradeAmount = order.trade_satoshis;
93+
return Math.floor(tradeAmount - tradeAmount * (lightning.routingBudgetPPM / 1000000));
9594
};
9695

9796
const validateInvoice = function (invoice: string, targetAmount: number) {
9897
const invoiceAmount = Number(invoice.substring(4, 5 + Math.floor(Math.log10(targetAmount))));
99-
if (targetAmount != invoiceAmount) {
98+
if (targetAmount != invoiceAmount && invoice.length > 20) {
10099
return 'Invalid invoice amount';
101100
} else {
102101
return '';
@@ -196,20 +195,23 @@ export const LightningPayoutForm = ({
196195
};
197196

198197
const onRoutingBudgetChange = function (e) {
198+
const tradeAmount = order.trade_satoshis;
199199
if (isFinite(e.target.value) && e.target.value >= 0) {
200200
let routingBudgetSats;
201201
let routingBudgetPPM;
202202

203203
if (lightning.routingBudgetUnit === 'Sats') {
204204
routingBudgetSats = Math.floor(e.target.value);
205-
routingBudgetPPM = Math.round((routingBudgetSats * 1000000) / lightning.amount);
205+
routingBudgetPPM = Math.round((routingBudgetSats * 1000000) / tradeAmount);
206206
} else {
207207
routingBudgetPPM = e.target.value;
208208
routingBudgetSats = Math.ceil((lightning.amount / 1000000) * routingBudgetPPM);
209209
}
210210

211211
if (routingBudgetPPM < 99999) {
212-
const amount = lightning.amount - routingBudgetSats;
212+
const amount = Math.floor(
213+
tradeAmount - tradeAmount * (lightning.routingBudgetPPM / 1000000),
214+
);
213215
setLightning({ ...lightning, routingBudgetSats, routingBudgetPPM, amount });
214216
}
215217
}

frontend/src/components/TradeBox/TradeSummary.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,9 @@ const TradeSummary = ({
273273
</ListItemIcon>
274274
<ListItemText
275275
primary={t('{{routingFeeSats}} MiliSats', {
276-
routingFeeSats: pn(platformSummary.routing_fee_sats),
276+
routingFeeSats: pn(platformSummary.routing_budget_sats),
277277
})}
278-
secondary={t('Platform covered routing fee')}
278+
secondary={t('Routing budget')}
279279
/>
280280
</ListItem>
281281

0 commit comments

Comments
 (0)