Bug fix invoice follow. Copy user token button.

This commit is contained in:
Reckless_Satoshi 2022-01-18 05:20:19 -08:00
parent a005b3509d
commit e31bc1adad
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
9 changed files with 111 additions and 64 deletions

View File

@ -9,9 +9,12 @@ REDIS_URL=''
# List of market price public APIs. If the currency is available in more than 1 API, will use median price.
MARKET_PRICE_APIS = https://blockchain.info/ticker, https://api.yadio.io/exrates/BTC
# Host e.g. robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
# Host e.g. robosats.com
HOST_NAME = ''
# e.g. robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
ONION_LOCATION = ''
# Trade fee in percentage %
FEE = 0.002
# Bond size in percentage %

View File

@ -31,8 +31,8 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
@admin.register(LNPayment)
class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
list_display = ('id','concept','status','num_satoshis','type','expires_at','sender_link','receiver_link')
list_display_links = ('id','concept')
list_display = ('id','concept','status','num_satoshis','type','expires_at','sender_link','receiver_link','order_made','order_taken','order_escrow','order_paid')
list_display_links = ('id','concept','order_made','order_taken','order_escrow','order_paid')
change_links = ('sender','receiver')
list_filter = ('type','concept','status')

View File

@ -446,7 +446,9 @@ class Logics():
@classmethod
def is_maker_bond_locked(cls, order):
if LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
if order.maker_bond.status == LNPayment.Status.LOCKED:
return True
elif LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
order.maker_bond.status = LNPayment.Status.LOCKED
order.maker_bond.save()
cls.publish_order(order)
@ -524,7 +526,7 @@ class Logics():
def is_taker_bond_locked(cls, order):
if order.taker_bond.status == LNPayment.Status.LOCKED:
return True
if LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
elif LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
cls.finalize_contract(order)
return True
return False
@ -574,20 +576,25 @@ class Logics():
order.save()
return True, {'bond_invoice': hold_payment['invoice'], 'bond_satoshis': bond_satoshis}
def trade_escrow_received(order):
''' Moves the order forward'''
# If status is 'Waiting for both' move to Waiting for invoice
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.WFE:
order.status = Order.Status.CHA
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
order.save()
@classmethod
def is_trade_escrow_locked(cls, order):
if LNNode.validate_hold_invoice_locked(order.trade_escrow.payment_hash):
if order.trade_escrow.status == LNPayment.Status.LOCKED:
return True
elif LNNode.validate_hold_invoice_locked(order.trade_escrow.payment_hash):
order.trade_escrow.status = LNPayment.Status.LOCKED
order.trade_escrow.save()
# If status is 'Waiting for both' move to Waiting for invoice
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.WFE:
order.status = Order.Status.CHA
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.CHA])
order.save()
cls.trade_escrow_received(order)
return True
return False
@ -607,7 +614,7 @@ class Logics():
elif order.trade_escrow.status == LNPayment.Status.INVGEN:
return True, {'escrow_invoice':order.trade_escrow.invoice, 'escrow_satoshis':order.trade_escrow.num_satoshis}
# If there was no taker_bond object yet, generates one
# If there was no taker_bond object yet, generate one
escrow_satoshis = order.last_satoshis # Amount was fixed when taker bond was locked
description = f"RoboSats - Escrow amount for '{str(order)}' - The escrow will be released to the buyer once you confirm you received the fiat. It will automatically return if buyer does not confirm the payment."

View File

@ -22,6 +22,7 @@ class Command(BaseCommand):
'''
help = 'Follows all active hold invoices'
rest = 5 # seconds between consecutive checks for invoice updates
# def add_arguments(self, parser):
# parser.add_argument('debug', nargs='+', type=boolean)
@ -40,7 +41,7 @@ class Command(BaseCommand):
stub = LNNode.invoicesstub
while True:
time.sleep(5)
time.sleep(self.rest)
# time it for debugging
t0 = time.time()
@ -95,21 +96,24 @@ class Command(BaseCommand):
def update_order_status(self, lnpayment):
''' Background process following LND hold invoices
might catch LNpayments changing status. If they do,
can catch LNpayments changing status. If they do,
the order status might have to change status too.'''
# If the LNPayment goes to LOCKED (ACCEPTED)
if lnpayment.status == LNPayment.Status.LOCKED:
# It is a maker bond => Publish order.
order = lnpayment.order_made
if not order == None:
Logics.publish_order(order)
if not lnpayment.order_made == None:
Logics.publish_order(lnpayment.order_made)
return
# It is a taker bond => close contract.
order = lnpayment.order_taken
if not order == None:
if order.status == Order.Status.TAK:
Logics.finalize_contract(order)
return
elif not lnpayment.order_taken == None:
if lnpayment.order_taken.status == Order.Status.TAK:
Logics.finalize_contract(lnpayment.order_taken)
return
# It is a trade escrow => move foward order status.
elif not lnpayment.order_escrow == None:
Logics.trade_escrow_received(lnpayment.order_escrow)
return

View File

@ -151,7 +151,7 @@ class Order(models.Model):
trade_escrow = models.OneToOneField(LNPayment, related_name='order_escrow', on_delete=models.SET_NULL, null=True, default=None, blank=True)
# buyer payment LN invoice
buyer_invoice = models.ForeignKey(LNPayment, related_name='buyer_invoice', on_delete=models.SET_NULL, null=True, default=None, blank=True)
buyer_invoice = models.OneToOneField(LNPayment, related_name='order_paid', on_delete=models.SET_NULL, null=True, default=None, blank=True)
# ratings
maker_rated = models.BooleanField(default=False, null=False)
@ -180,9 +180,8 @@ class Order(models.Model):
}
def __str__(self):
# Make relational back to ORDER
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}')
@receiver(pre_delete, sender=Order)
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):

View File

@ -40,7 +40,7 @@ def follow_send_payment(lnpayment):
from base64 import b64decode
from api.lightning.node import LNNode
from api.models import LNPayment
from api.models import LNPayment, Order
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
@ -48,25 +48,37 @@ def follow_send_payment(lnpayment):
request = LNNode.routerrpc.SendPaymentRequest(
payment_request=lnpayment.invoice,
fee_limit_sat=fee_limit_sat,
timeout_seconds=60)
timeout_seconds=60) # time out payment in 60 seconds
order = lnpayment.order_paid
for response in LNNode.routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
if response.status == 0 : # Status 0 'UNKNOWN'
# Not sure when this status happens
pass
if response.status == 1 : # Status 1 'IN_FLIGHT'
print('IN_FLIGHT')
lnpayment.status = LNPayment.Status.FLIGHT
lnpayment.save()
order.status = Order.Status.PAY
order.save()
if response.status == 3 : # Status 3 'FAILED'
print('FAILED')
lnpayment.status = LNPayment.Status.FAILRO
lnpayment.save()
order.status = Order.Status.FAI
order.save()
context = LNNode.payment_failure_context[response.failure_reason]
# Call for a retry here
return False, context
if response.status == 2 : # Status 2 'SUCCEEDED'
print('SUCCEEDED')
lnpayment.status = LNPayment.Status.SUCCED
lnpayment.save()
order.status = Order.Status.SUC
order.save()
return True, None
@shared_task(name="cache_external_market_prices", ignore_result=True)

View File

@ -5,7 +5,7 @@ import numpy as np
market_cache = {}
@ring.dict(market_cache, expire=5) #keeps in cache for 5 seconds
@ring.dict(market_cache, expire=3) #keeps in cache for 3 seconds
def get_exchange_rates(currencies):
'''
Params: list of currency codes.

View File

@ -63,7 +63,7 @@ export default class OrderPage extends Component {
"10": 15000, //'Fiat sent - In chatroom'
"11": 60000, //'In dispute'
"12": 9999999,//'Collaboratively cancelled'
"13": 120000, //'Sending satoshis to buyer'
"13": 3000, //'Sending satoshis to buyer'
"14": 9999999,//'Sucessful trade'
"15": 10000, //'Failed lightning network routing'
"16": 9999999,//'Maker lost dispute'

View File

@ -1,8 +1,10 @@
import React, { Component } from "react";
import { Button , Dialog, Grid, Typography, TextField, ButtonGroup} from "@mui/material"
import { Button , Dialog, Grid, Typography, TextField, ButtonGroup, CircularProgress, IconButton} from "@mui/material"
import { Link } from 'react-router-dom'
import Image from 'material-ui-image'
import InfoDialog from './InfoDialog'
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import ContentCopy from "@mui/icons-material/ContentCopy";
function getCookie(name) {
let cookieValue = null;
@ -27,6 +29,7 @@ export default class UserGenPage extends Component {
this.state = {
token: this.genBase62Token(34),
openInfo: false,
showRobosat: true,
};
this.getGeneratedUser(this.state.token);
}
@ -53,6 +56,7 @@ export default class UserGenPage extends Component {
shannon_entropy: data.token_shannon_entropy,
bad_request: data.bad_request,
found: data.found,
showRobosat:true,
});
});
}
@ -69,10 +73,12 @@ export default class UserGenPage extends Component {
handleAnotherButtonPressed=(e)=>{
this.delGeneratedUser()
this.setState({
token: this.genBase62Token(34),
});
this.getGeneratedUser(this.state.token);
// this.setState({
// showRobosat: false,
// token: this.genBase62Token(34),
// });
// this.getGeneratedUser(this.state.token);
window.location.reload();
}
handleChangeToken=(e)=>{
@ -81,6 +87,7 @@ export default class UserGenPage extends Component {
token: e.target.value,
})
this.getGeneratedUser(e.target.value);
this.setState({showRobosat: false})
}
handleClickOpenInfo = () => {
@ -109,20 +116,26 @@ export default class UserGenPage extends Component {
render() {
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h5" variant="h5">
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
</Typography>
</Grid>
<Grid item xs={12} align="center">
<div style={{ maxWidth: 200, maxHeight: 200 }}>
<Image className='newAvatar'
disableError='true'
cover='true'
color='null'
src={this.state.avatar_url}
/>
</div><br/>
<Grid item xs={12} align="center">
{this.state.showRobosat ?
<div>
<Grid item xs={12} align="center">
<Typography component="h5" variant="h5">
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
</Typography>
</Grid>
<Grid item xs={12} align="center">
<div style={{ maxWidth: 200, maxHeight: 200 }}>
<Image className='newAvatar'
disableError='true'
cover='true'
color='null'
src={this.state.avatar_url}
/>
</div><br/>
</Grid>
</div>
: <CircularProgress />}
</Grid>
{
this.state.found ?
@ -134,21 +147,30 @@ export default class UserGenPage extends Component {
:
""
}
<Grid item xs={12} align="center">
<TextField
error={this.state.bad_request}
label='Token - Store safely'
required='true'
value={this.state.token}
variant='standard'
helperText={this.state.bad_request}
size='small'
// multiline = {true}
onChange={this.handleChangeToken}
/>
<Grid container align="center">
<Grid item xs={12} align="center">
<IconButton onClick= {()=>navigator.clipboard.writeText(this.state.token)}>
<ContentCopy color='secondary'/>
</IconButton>
<TextField
//sx={{ input: { color: 'purple' } }}
InputLabelProps={{
style: { color: 'purple' },
}}
error={this.state.bad_request}
label='Token - Store safely'
required='true'
value={this.state.token}
variant='standard'
helperText={this.state.bad_request}
size='small'
// multiline = {true}
onChange={this.handleChangeToken}
/>
</Grid>
</Grid>
<Grid item xs={12} align="center">
<Button onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
<Button size='small' onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
</Grid>
<Grid item xs={12} align="center">
<ButtonGroup variant="contained" aria-label="outlined primary button group">