diff --git a/api/logics.py b/api/logics.py index 7059ea10..e3fd6f55 100644 --- a/api/logics.py +++ b/api/logics.py @@ -514,8 +514,7 @@ class Logics(): # Do not gen and kick out the taker if order is older than expiry time if order.expires_at < timezone.now(): - cls.cancel_bond(order.taker_bond) - cls.kick_taker(order) + cls.order_expires(order) return False, {'bad_request':'Invoice expired. You did not confirm taking the order in time.'} # Do not gen if a taker invoice exist. Do not return if it is already locked. Return the old one if still waiting. @@ -580,7 +579,7 @@ class Logics(): # Do not generate if escrow deposit time has expired if order.expires_at < timezone.now(): - cls.cancel_order(order,user) + cls.order_expires(order) return False, {'bad_request':'Invoice expired. You did not send the escrow in time.'} # Do not gen if an escrow invoice exist. Do not return if it is already locked. Return the old one if still waiting. @@ -690,12 +689,10 @@ class Logics(): # If buyer, settle escrow and mark fiat sent if cls.is_buyer(order, user): - if cls.settle_escrow(order): ##### !!! KEY LINE - SETTLES THE TRADE ESCROW !!! - order.trade_escrow.status = LNPayment.Status.SETLED - order.status = Order.Status.FSE - order.is_fiat_sent = True + order.status = Order.Status.FSE + order.is_fiat_sent = True - # If seller and fiat sent, pay buyer invoice + # If seller and fiat was sent, SETTLE ESCRO AND PAY BUYER INVOICE elif cls.is_seller(order, user): if not order.is_fiat_sent: return False, {'bad_request':'You cannot confirm to have received the fiat before it is confirmed to be sent by the buyer.'} @@ -703,6 +700,9 @@ class Logics(): # 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: return False, {'bad_request':'Woah, something broke badly. Report in the public channels, or open a Github Issue.'} + + if cls.settle_escrow(order): ##### !!! KEY LINE - SETTLES THE TRADE ESCROW !!! + order.trade_escrow.status = LNPayment.Status.SETLED # Double check the escrow is settled. if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash): diff --git a/api/management/commands/clean_orders.py b/api/management/commands/clean_orders.py index 033784c5..6e5faf81 100644 --- a/api/management/commands/clean_orders.py +++ b/api/management/commands/clean_orders.py @@ -15,6 +15,8 @@ class Command(BaseCommand): ''' Continuously checks order expiration times for 1 hour. If order has expires, it calls the logics module for expiration handling.''' + # TODO handle 'database is locked' + do_nothing = [Order.Status.DEL, Order.Status.UCA, Order.Status.EXP, Order.Status.FSE, Order.Status.DIS, Order.Status.CCA, @@ -34,8 +36,17 @@ class Command(BaseCommand): for idx, order in enumerate(queryset): context = str(order)+ " was "+ Order.Status(order.status).label - if Logics.order_expires(order): # Order send to expire here - debug['expired_orders'].append({idx:context}) + try: + if Logics.order_expires(order): # Order send to expire here + debug['expired_orders'].append({idx:context}) + + # If it cannot locate the hold invoice, make it expire anywway + except Exception as e: + if 'unable to locate invoice' in str(e): + self.stdout.write(str(e)) + order.status = Order.Status.EXP + debug['expired_orders'].append({idx:context}) + if debug['num_expired_orders'] > 0: self.stdout.write(str(timezone.now())) diff --git a/api/management/commands/follow_invoices.py b/api/management/commands/follow_invoices.py index 52253a2a..5a9840bd 100644 --- a/api/management/commands/follow_invoices.py +++ b/api/management/commands/follow_invoices.py @@ -31,6 +31,8 @@ class Command(BaseCommand): ''' Follows and updates LNpayment objects until settled or canceled''' + # TODO handle 'database is locked' + lnd_state_to_lnpayment_status = { 0: LNPayment.Status.INVGEN, # OPEN 1: LNPayment.Status.SETLED, # SETTLED @@ -64,6 +66,7 @@ class Command(BaseCommand): # If it fails at finding the invoice it has been canceled. # On RoboSats DB we make a distinction between cancelled and returned (LND does not) if 'unable to locate invoice' in str(e): + self.stdout.write('unable to locate invoice') hold_lnpayment.status = LNPayment.Status.CANCEL # LND restarted. if 'wallet locked, unlock it' in str(e): diff --git a/api/tasks.py b/api/tasks.py index 82da436a..08bad68a 100644 --- a/api/tasks.py +++ b/api/tasks.py @@ -70,7 +70,7 @@ def follow_send_payment(lnpayment): order.status = Order.Status.FAI order.save() context = LNNode.payment_failure_context[response.failure_reason] - # Call for a retry here + # Call a retry here? return False, context if response.status == 2 : # Status 2 'SUCCEEDED' diff --git a/frontend/src/components/InfoDialog.js b/frontend/src/components/InfoDialog.js index d7e8adae..f48ac387 100644 --- a/frontend/src/components/InfoDialog.js +++ b/frontend/src/components/InfoDialog.js @@ -19,7 +19,7 @@ export default class InfoDialog extends Component { How does it work? -

AdequateAlice01 wants to sell bitcoin. She posts a sell order. +

Anonymous AdequateAlice01 wants to sell bitcoin. She posts a sell order. BafflingBob02 wants to buy bitcoin and he takes Alice's order. Both have to post a small bond using lightning to prove they are real robots. Then, Alice posts the trade collateral also using a lightning @@ -51,8 +51,9 @@ export default class InfoDialog extends Component { Is RoboSats private? -

RoboSats will never ask you for your name, country or ID. For - best anonymity use Tor Browser and access the .onion hidden service.

+

RoboSats will never ask you for your name, country or ID. RoboSats does + not custody your funds, and doesn't care who you are. For best anonymity + use Tor Browser and access the .onion hidden service.

Your trading peer is the only one who can potentially guess anything about you. Keep your chat short and concise. Avoid @@ -73,29 +74,33 @@ export default class InfoDialog extends Component { What is the trust model?

The buyer and the seller never have to trust each other. - Some trust on RoboSats staff is needed since linking - the seller's hold invoice and buyer payment is not atomic (yet). + Some trust on RoboSats is needed since linking the + seller's hold invoice and buyer payment is not atomic (yet). In addition, disputes are solved by the RoboSats staff.

-

While trust requirements are minimized, RoboSats could - run away with your satoshis. It could be argued that it is not - worth it, as it would instantly destroy RoboSats reputation. +

Trust requirements are minimized, however there is still one way + RoboSats could run away with your satoshis, by not releasing + the satoshis to the buyer. It could be argued that such move is not on + RoboSats interest as it would damage thereputation for a small payout. However, you should hesitate and only trade small quantities at a time. For large amounts use an onchain escrow service such as Bisq

You can build more trust on RoboSats by - inspecting the source code

+ inspecting the source code.

What happens if RoboSats suddenly disapears? -

Your sats will most likely return to you. Any hold invoice that is not +

Your sats will return to you. Any hold invoice that is not settled would be automatically returned even if RoboSats goes down forever. This is true for both, locked bonds and trading escrows. However, - there is a small window between the buyer confirms FIAT SENT and the moment - the seller releases the satoshis when the funds could be lost. + there is a small window between the seller confirms FIAT RECEIVED and the moment + the buyer receives the satoshis when the funds could be permanentely lost if + RoboSats disappears. This window is about 1 second long. Make sure to have enough + inbound liquidity to avoid routing failures. If you have any problem, reach out + trough the RoboSats public channels.

@@ -116,9 +121,7 @@ export default class InfoDialog extends Component { RoboSats will definitely never ask for your robot token.

- - ) } diff --git a/frontend/src/components/TradeBox.js b/frontend/src/components/TradeBox.js index 1c25c9f1..b4c865dd 100644 --- a/frontend/src/components/TradeBox.js +++ b/frontend/src/components/TradeBox.js @@ -161,7 +161,7 @@ export default class TradeBox extends Component { size="small" defaultValue={this.props.data.bondInvoice} disabled="true" - helperText="This is a hold invoice. It will be charged only if you cancel or lose a dispute." + helperText="This is a hold invoice, it will freeze in your wallet. It will be charged only if you cancel or lose a dispute." color = "secondary" /> @@ -198,7 +198,7 @@ export default class TradeBox extends Component { size="small" defaultValue={this.props.data.escrowInvoice} disabled="true" - helperText="This is a hold invoice. It will be charged once the buyer confirms he sent the fiat." + helperText={"This is a hold invoice, it will freeze in your wallet. It will be released to the buyer once you confirm to have received the "+this.props.data.currencyCode+"."} color = "secondary" /> diff --git a/robosats/celery/__init__.py b/robosats/celery/__init__.py index a176b226..0d1999d2 100644 --- a/robosats/celery/__init__.py +++ b/robosats/celery/__init__.py @@ -31,9 +31,9 @@ app.conf.beat_scheduler = 'django_celery_beat.schedulers:DatabaseScheduler' # Configure the periodic tasks app.conf.beat_schedule = { - 'users-cleansing': { # Cleans abandoned users every hour + 'users-cleansing': { # Cleans abandoned users every 6 hours 'task': 'users_cleansing', - 'schedule': timedelta(hours=1), + 'schedule': timedelta(hours=6), }, 'cache-market-prices': { # Cache market prices every minutes for now. 'task': 'cache_external_market_prices',