From 2523e5ef0990c7c39516e2c8df42ebbbf7642e50 Mon Sep 17 00:00:00 2001
From: Reckless_Satoshi
Date: Wed, 12 Jan 2022 04:57:03 -0800
Subject: [PATCH] Add needed LNNode routines and LNpayments status. First full
trade demonstrated
---
api/admin.py | 2 +-
api/lightning/node.py | 20 +++++++++-----
api/logics.py | 33 ++++++++++++++---------
api/models.py | 10 ++++---
api/serializers.py | 2 +-
api/views.py | 3 ++-
frontend/src/components/OrderPage.js | 7 ++++-
frontend/src/components/TradeBox.js | 40 ++++++++++++++--------------
8 files changed, 71 insertions(+), 46 deletions(-)
diff --git a/api/admin.py b/api/admin.py
index a5b32506..9d8ab64f 100644
--- a/api/admin.py
+++ b/api/admin.py
@@ -26,7 +26,7 @@ class EUserAdmin(UserAdmin):
class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
list_display = ('id','type','maker_link','taker_link','status','amount','currency','t0_satoshis','is_disputed','is_fiat_sent','created_at','expires_at', 'buyer_invoice_link','maker_bond_link','taker_bond_link','trade_escrow_link')
list_display_links = ('id','type')
- change_links = ('maker','taker','buyer_invoice','maker_bond','taker_invoice','taker_bond','trade_escrow')
+ change_links = ('maker','taker','buyer_invoice','maker_bond','taker_bond','trade_escrow')
list_filter = ('is_disputed','is_fiat_sent','type','currency','status')
@admin.register(LNPayment)
diff --git a/api/lightning/node.py b/api/lightning/node.py
index fd2fdb49..6da8f0eb 100644
--- a/api/lightning/node.py
+++ b/api/lightning/node.py
@@ -83,6 +83,8 @@ class LNNode():
'''Checks if hold invoice is locked'''
request = invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
response = cls.invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
+ print('status here')
+ print(response.state)
return response.state == 3 # True if hold invoice is accepted.
@classmethod
@@ -142,32 +144,38 @@ class LNNode():
buyer_invoice['description'] = payreq_decoded.description
buyer_invoice['payment_hash'] = payreq_decoded.payment_hash
+
return buyer_invoice
@classmethod
def pay_invoice(cls, invoice, num_satoshis):
'''Sends sats to buyer'''
- # Needs router subservice
- # Maybe best to pass order and change status live.
fee_limit_sat = max(num_satoshis * 0.0002, 10) # 200 ppm or 10 sats
-
request = routerrpc.SendPaymentRequest(
payment_request=invoice,
- amt_msat=num_satoshis,
fee_limit_sat=fee_limit_sat,
timeout_seconds=60)
for response in cls.routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
- if response.status == True:
+ print(response)
+ print(response.status)
+ print(response.grpc_status)
+ if response.status == 1 : # Status 1 'IN_FLIGHT'
+ pass # LIVE UPDATE THE order.lnpayment.status
+ if response.status == 'FAILED':
+ pass # LIVE UPDATE THE order.lnpayment.status
+ if response.status == 2 : # STATUS 'SUCCEEDED'
return True
+ # How to catch the errors like:"grpc_message":"invoice is already paid","grpc_status":6}
+ # These are not in the response only printed to commandline
return False
@classmethod
def double_check_htlc_is_settled(cls, payment_hash):
''' Just as it sounds. Better safe than sorry!'''
- request = invoicesrpc.LookupInvoiceMsg(payment_hash=payment_hash)
+ request = invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
response = cls.invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
return response.state == 1 # LND states: 0 OPEN, 1 SETTLED, 3 ACCEPTED, GRPC_ERROR status 5 when cancelled/returned
diff --git a/api/logics.py b/api/logics.py
index ed88b2b5..66f6cc6e 100644
--- a/api/logics.py
+++ b/api/logics.py
@@ -166,13 +166,16 @@ class Logics():
)
# If the order status is 'Waiting for invoice'. Move forward to 'chat'
- if order.status == Order.Status.WFI: order.status = Order.Status.CHA
+ if order.status == Order.Status.WFI:
+ order.status = Order.Status.CHA
+ order.expires_at = timezone.now() + timedelta(hours=FIAT_EXCHANGE_DURATION)
# If the order status is 'Waiting for both'. Move forward to 'waiting for escrow'
if order.status == Order.Status.WF2:
- if order.trade_escrow:
- if order.trade_escrow.status == LNPayment.Status.LOCKED:
- order.status = Order.Status.CHA
+ # If the escrow is lock move to Chat.
+ if order.trade_escrow.status == LNPayment.Status.LOCKED:
+ order.status = Order.Status.CHA
+ order.expires_at = timezone.now() + timedelta(hours=FIAT_EXCHANGE_DURATION)
else:
order.status = Order.Status.WFE
@@ -312,7 +315,7 @@ class Logics():
order.last_satoshis = cls.satoshis_now(order)
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
- description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
+ description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel."
# Gen hold Invoice
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
@@ -368,7 +371,7 @@ class Logics():
# If there was no taker_bond object yet, generates one
order.last_satoshis = cls.satoshis_now(order)
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
- description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
+ description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel."
# Gen hold Invoice
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
@@ -400,7 +403,7 @@ class Logics():
if order.status == Order.Status.WF2:
order.status = Order.Status.WFI
# If status is 'Waiting for invoice' move to Chat
- elif order.status == Order.Status.WFI:
+ elif order.status == Order.Status.WFE:
order.status = Order.Status.CHA
order.expires_at = timezone.now() + timedelta(hours=FIAT_EXCHANGE_DURATION)
order.save()
@@ -470,11 +473,17 @@ class Logics():
order.taker_bond.status = LNPayment.Status.SETLED
order.taker_bond.save()
return True
+
+ def return_bond(bond):
+ '''returns a bond'''
+ if LNNode.cancel_return_hold_invoice(bond.payment_hash):
+ bond.status = LNPayment.Status.RETNED
+ return True
def pay_buyer_invoice(order):
''' Pay buyer invoice'''
# TODO ERROR HANDLING
- if LNNode.pay_invoice(order.buyer_invoice.invoice):
+ if LNNode.pay_invoice(order.buyer_invoice.invoice, order.buyer_invoice.num_satoshis):
return True
@classmethod
@@ -498,18 +507,18 @@ class Logics():
return False, {'bad_request':'You cannot confirm to have received the fiat before it is confirmed to be sent by the buyer.'}
# Make sure the trade escrow is at least as big as the buyer invoice
- if order.trade_escrow.num_satoshis > order.buyer_invoice.num_satoshis:
+ if order.trade_escrow.num_satoshis <= order.buyer_invoice.num_satoshis:
return False, {'bad_request':'Woah, something broke badly. Report in the public channels, or open a Github Issue.'}
# Double check the escrow is settled.
if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash):
if cls.pay_buyer_invoice(order): ##### !!! KEY LINE - PAYS THE BUYER INVOICE !!!
order.status = Order.Status.PAY
- order.buyer_invoice.status = LNPayment.Status.PAYING
+ order.buyer_invoice.status = LNPayment.Status.SETLED
# RETURN THE BONDS
- LNNode.cancel_return_hold_invoice(order.taker_bond.payment_hash)
- LNNode.cancel_return_hold_invoice(order.maker_bond.payment_hash)
+ cls.return_bond(order.taker_bond)
+ cls.return_bond(order.maker_bond)
else:
return False, {'bad_request':'You cannot confirm the fiat payment at this stage'}
diff --git a/api/models.py b/api/models.py
index fcd0190d..ace4cdf4 100644
--- a/api/models.py
+++ b/api/models.py
@@ -34,8 +34,10 @@ class LNPayment(models.Model):
RETNED = 3, 'Returned'
EXPIRE = 4, 'Expired'
VALIDI = 5, 'Valid'
- FLIGHT = 6, 'On flight'
- FAILRO = 7, 'Routing failed'
+ FLIGHT = 6, 'In flight'
+ SUCCED = 7, 'Succeeded'
+ FAILRO = 8, 'Routing failed'
+
# payment use details
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HOLD)
@@ -44,10 +46,10 @@ class LNPayment(models.Model):
routing_retries = models.PositiveSmallIntegerField(null=False, default=0)
# payment info
- invoice = models.CharField(max_length=1000, unique=True, null=True, default=None, blank=True)
+ invoice = models.CharField(max_length=1200, unique=True, null=True, default=None, blank=True) # Some invoices with lots of routing hints might be long
payment_hash = models.CharField(max_length=100, unique=True, null=True, default=None, blank=True)
preimage = models.CharField(max_length=64, unique=True, null=True, default=None, blank=True)
- description = models.CharField(max_length=200, unique=False, null=True, default=None, blank=True)
+ description = models.CharField(max_length=500, unique=False, null=True, default=None, blank=True)
num_satoshis = models.PositiveBigIntegerField(validators=[MinValueValidator(MIN_TRADE*BOND_SIZE), MaxValueValidator(MAX_TRADE*(1+BOND_SIZE+FEE))])
created_at = models.DateTimeField()
expires_at = models.DateTimeField()
diff --git a/api/serializers.py b/api/serializers.py
index a819db91..6beff335 100644
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -12,6 +12,6 @@ class MakeOrderSerializer(serializers.ModelSerializer):
fields = ('type','currency','amount','payment_method','is_explicit','premium','satoshis')
class UpdateOrderSerializer(serializers.Serializer):
- invoice = serializers.CharField(max_length=300, allow_null=True, allow_blank=True, default=None)
+ invoice = serializers.CharField(max_length=2000, allow_null=True, allow_blank=True, default=None)
action = serializers.ChoiceField(choices=('take','update_invoice','dispute','cancel','confirm','rate'), allow_null=False)
rating = serializers.ChoiceField(choices=('1','2','3','4','5'), allow_null=True, allow_blank=True, default=None)
\ No newline at end of file
diff --git a/api/views.py b/api/views.py
index b4d3ec70..755a654e 100644
--- a/api/views.py
+++ b/api/views.py
@@ -137,7 +137,7 @@ class OrderView(viewsets.ViewSet):
# If both bonds are locked, participants can see the final trade amount in sats.
if order.taker_bond:
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
- # Seller sees the amount he pays
+ # Seller sees the amount he sends
if data['is_seller']:
data['trade_satoshis'] = order.last_satoshis
# Buyer sees the amount he receives
@@ -227,6 +227,7 @@ class OrderView(viewsets.ViewSet):
# 2) If action is 'update invoice'
if action == 'update_invoice' and invoice:
+ print('AAAAAAAAAAAAAAAAAAAAAAAAAAAA')
valid, context = Logics.update_invoice(order,request.user,invoice)
if not valid: return Response(context, status.HTTP_400_BAD_REQUEST)
diff --git a/frontend/src/components/OrderPage.js b/frontend/src/components/OrderPage.js
index 5fb3c490..14b04cbf 100644
--- a/frontend/src/components/OrderPage.js
+++ b/frontend/src/components/OrderPage.js
@@ -284,7 +284,12 @@ export default class OrderPage extends Component {
:""}
-
+ {this.state.isMaker & this.state.statusCode > 0 & this.state.statusCode < 9 ?
+
+ Cancelling now forfeits the maker bond
+
+ :""}
+
{/* Takers can cancel before commiting the bond (status 3)*/}
{this.state.isTaker & this.state.statusCode == 3 ?
diff --git a/frontend/src/components/TradeBox.js b/frontend/src/components/TradeBox.js
index f27ef3e3..5a9ceb2b 100644
--- a/frontend/src/components/TradeBox.js
+++ b/frontend/src/components/TradeBox.js
@@ -70,6 +70,16 @@ export default class TradeBox extends Component {
);
}
+ showBondIsLocked=()=>{
+ return (
+
+
+ 🔒 Your {this.props.data.isMaker ? 'maker' : 'taker'} bond is safely locked
+
+
+ );
+ }
+
showEscrowQRInvoice=()=>{
return (
@@ -92,6 +102,7 @@ export default class TradeBox extends Component {
color = "secondary"
/>
+ {this.showBondIsLocked()}
);
}
@@ -113,6 +124,7 @@ export default class TradeBox extends Component {
Please wait for the taker to confirm his commitment by locking a bond.
+ {this.showBondIsLocked()}
);
}
@@ -138,12 +150,6 @@ export default class TradeBox extends Component {
return to you (no action needed).
-
-
-
- 🔒 Your maker bond is safely locked
-
-
{/* TODO API sends data for a more confortable wait */}
@@ -159,8 +165,11 @@ export default class TradeBox extends Component {
+
+
+ {this.showBondIsLocked()}
)
}
@@ -225,6 +234,7 @@ export default class TradeBox extends Component {
+ {this.showBondIsLocked()}
)
}
@@ -245,6 +255,7 @@ export default class TradeBox extends Component {
you will get your bond back automatically.
+ {this.showBondIsLocked()}
)
}
@@ -266,6 +277,7 @@ export default class TradeBox extends Component {
you will get back the trade collateral and your bond automatically.
+ {this.showBondIsLocked()}
)
}
@@ -322,22 +334,18 @@ handleRatingChange=(e)=>{
// TODO, show alert and ask for double confirmation (Have you check you received the fiat? Confirming fiat received settles the trade.)
// Ask for double confirmation.
return(
-
-
)
}
showOpenDisputeButton(){
// TODO, show alert about how opening a dispute might involve giving away personal data and might mean losing the bond. Ask for double confirmation.
return(
-
-
+
-
)
}
@@ -368,19 +376,11 @@ handleRatingChange=(e)=>{
{receivedFiatButton ? this.showFiatReceivedButton() : ""}
{openDisputeButton ? this.showOpenDisputeButton() : ""}
+ {this.showBondIsLocked()}
)
}
-
- // showFiatReceivedButton(){
-
- // }
-
- // showOpenDisputeButton(){
-
- // }
-
showRateSelect(){
return(