Logics update: escrow settles exactly at buyer payout time.

This commit is contained in:
Reckless_Satoshi 2022-01-19 11:37:10 -08:00
parent bfa0cd84d1
commit 7218b9b0d3
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
7 changed files with 46 additions and 29 deletions

View File

@ -514,8 +514,7 @@ class Logics():
# Do not gen and kick out the taker if order is older than expiry time # Do not gen and kick out the taker if order is older than expiry time
if order.expires_at < timezone.now(): if order.expires_at < timezone.now():
cls.cancel_bond(order.taker_bond) cls.order_expires(order)
cls.kick_taker(order)
return False, {'bad_request':'Invoice expired. You did not confirm taking the order in time.'} 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. # 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 # Do not generate if escrow deposit time has expired
if order.expires_at < timezone.now(): 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.'} 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. # 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 buyer, settle escrow and mark fiat sent
if cls.is_buyer(order, user): if cls.is_buyer(order, user):
if cls.settle_escrow(order): ##### !!! KEY LINE - SETTLES THE TRADE ESCROW !!! order.status = Order.Status.FSE
order.trade_escrow.status = LNPayment.Status.SETLED 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): elif cls.is_seller(order, user):
if not order.is_fiat_sent: 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.'} 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 # 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.'} 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. # Double check the escrow is settled.
if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash): if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash):

View File

@ -15,6 +15,8 @@ class Command(BaseCommand):
''' Continuously checks order expiration times for 1 hour. If order ''' Continuously checks order expiration times for 1 hour. If order
has expires, it calls the logics module for expiration handling.''' has expires, it calls the logics module for expiration handling.'''
# TODO handle 'database is locked'
do_nothing = [Order.Status.DEL, Order.Status.UCA, do_nothing = [Order.Status.DEL, Order.Status.UCA,
Order.Status.EXP, Order.Status.FSE, Order.Status.EXP, Order.Status.FSE,
Order.Status.DIS, Order.Status.CCA, Order.Status.DIS, Order.Status.CCA,
@ -34,8 +36,17 @@ class Command(BaseCommand):
for idx, order in enumerate(queryset): for idx, order in enumerate(queryset):
context = str(order)+ " was "+ Order.Status(order.status).label context = str(order)+ " was "+ Order.Status(order.status).label
if Logics.order_expires(order): # Order send to expire here try:
debug['expired_orders'].append({idx:context}) 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: if debug['num_expired_orders'] > 0:
self.stdout.write(str(timezone.now())) self.stdout.write(str(timezone.now()))

View File

@ -31,6 +31,8 @@ class Command(BaseCommand):
''' Follows and updates LNpayment objects ''' Follows and updates LNpayment objects
until settled or canceled''' until settled or canceled'''
# TODO handle 'database is locked'
lnd_state_to_lnpayment_status = { lnd_state_to_lnpayment_status = {
0: LNPayment.Status.INVGEN, # OPEN 0: LNPayment.Status.INVGEN, # OPEN
1: LNPayment.Status.SETLED, # SETTLED 1: LNPayment.Status.SETLED, # SETTLED
@ -64,6 +66,7 @@ class Command(BaseCommand):
# If it fails at finding the invoice it has been canceled. # 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) # On RoboSats DB we make a distinction between cancelled and returned (LND does not)
if 'unable to locate invoice' in str(e): if 'unable to locate invoice' in str(e):
self.stdout.write('unable to locate invoice')
hold_lnpayment.status = LNPayment.Status.CANCEL hold_lnpayment.status = LNPayment.Status.CANCEL
# LND restarted. # LND restarted.
if 'wallet locked, unlock it' in str(e): if 'wallet locked, unlock it' in str(e):

View File

@ -70,7 +70,7 @@ def follow_send_payment(lnpayment):
order.status = Order.Status.FAI order.status = Order.Status.FAI
order.save() order.save()
context = LNNode.payment_failure_context[response.failure_reason] context = LNNode.payment_failure_context[response.failure_reason]
# Call for a retry here # Call a retry here?
return False, context return False, context
if response.status == 2 : # Status 2 'SUCCEEDED' if response.status == 2 : # Status 2 'SUCCEEDED'

View File

@ -19,7 +19,7 @@ export default class InfoDialog extends Component {
<Typography component="h5" variant="h5">How does it work?</Typography> <Typography component="h5" variant="h5">How does it work?</Typography>
<Typography component="body2" variant="body2"> <Typography component="body2" variant="body2">
<p>AdequateAlice01 wants to sell bitcoin. She posts a sell order. <p>Anonymous AdequateAlice01 wants to sell bitcoin. She posts a sell order.
BafflingBob02 wants to buy bitcoin and he takes Alice's 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 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 robots. Then, Alice posts the trade collateral also using a lightning
@ -51,8 +51,9 @@ export default class InfoDialog extends Component {
<Typography component="h5" variant="h5">Is <i>RoboSats</i> private?</Typography> <Typography component="h5" variant="h5">Is <i>RoboSats</i> private?</Typography>
<Typography component="body2" variant="body2"> <Typography component="body2" variant="body2">
<p> RoboSats will never ask you for your name, country or ID. For <p> RoboSats will never ask you for your name, country or ID. RoboSats does
best anonymity use Tor Browser and access the .onion hidden service. </p> not custody your funds, and doesn't care who you are. For best anonymity
use Tor Browser and access the .onion hidden service. </p>
<p>Your trading peer is the only one who can potentially guess <p>Your trading peer is the only one who can potentially guess
anything about you. Keep your chat short and concise. Avoid anything about you. Keep your chat short and concise. Avoid
@ -73,29 +74,33 @@ export default class InfoDialog extends Component {
<Typography component="h5" variant="h5">What is the trust model?</Typography> <Typography component="h5" variant="h5">What is the trust model?</Typography>
<Typography component="body2" variant="body2"> <Typography component="body2" variant="body2">
<p> The buyer and the seller never have to trust each other. <p> The buyer and the seller never have to trust each other.
Some trust on <i>RoboSats</i> staff is needed since linking Some trust on <i>RoboSats</i> is needed since linking the
the seller's hold invoice and buyer payment is not atomic (yet). seller's hold invoice and buyer payment is not atomic (yet).
In addition, disputes are solved by the <i>RoboSats</i> staff. In addition, disputes are solved by the <i>RoboSats</i> staff.
</p> </p>
<p> While trust requirements are minimized, <i>RoboSats</i> could <p> Trust requirements are minimized, however there is still one way
run away with your satoshis. It could be argued that it is not <i>RoboSats</i> could run away with your satoshis, by not releasing
worth it, as it would instantly destroy <i>RoboSats</i> reputation. the satoshis to the buyer. It could be argued that such move is not on
<i>RoboSats</i> interest as it would damage thereputation for a small payout.
However, you should hesitate and only trade small quantities at a However, you should hesitate and only trade small quantities at a
time. For large amounts use an onchain escrow service such as <i>Bisq</i> time. For large amounts use an onchain escrow service such as <i>Bisq</i>
</p> </p>
<p> You can build more trust on <i>RoboSats</i> by <a href='https://github.com/reckless-satoshi/robosats'> <p> You can build more trust on <i>RoboSats</i> by <a href='https://github.com/reckless-satoshi/robosats'>
inspecting the source code </a> </p> inspecting the source code. </a> </p>
</Typography> </Typography>
<Typography component="h5" variant="h5">What happens if <i>RoboSats</i> suddenly disapears?</Typography> <Typography component="h5" variant="h5">What happens if <i>RoboSats</i> suddenly disapears?</Typography>
<Typography component="body2" variant="body2"> <Typography component="body2" variant="body2">
<p> Your sats will most likely return to you. Any hold invoice that is not <p> Your sats will return to you. Any hold invoice that is not
settled would be automatically returned even if <i>RoboSats</i> goes down settled would be automatically returned even if <i>RoboSats</i> goes down
forever. This is true for both, locked bonds and trading escrows. However, 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 there is a small window between the seller confirms FIAT RECEIVED and the moment
the seller releases the satoshis when the funds could be lost. the buyer receives the satoshis when the funds could be permanentely lost if
<i>RoboSats</i> 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 <i>RoboSats</i> public channels.
</p> </p>
</Typography> </Typography>
@ -116,9 +121,7 @@ export default class InfoDialog extends Component {
RoboSats</i> will definitely never ask for your robot token. RoboSats</i> will definitely never ask for your robot token.
</p> </p>
</Typography> </Typography>
</DialogContent> </DialogContent>
</div> </div>
) )
} }

View File

@ -161,7 +161,7 @@ export default class TradeBox extends Component {
size="small" size="small"
defaultValue={this.props.data.bondInvoice} defaultValue={this.props.data.bondInvoice}
disabled="true" 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" color = "secondary"
/> />
</Grid> </Grid>
@ -198,7 +198,7 @@ export default class TradeBox extends Component {
size="small" size="small"
defaultValue={this.props.data.escrowInvoice} defaultValue={this.props.data.escrowInvoice}
disabled="true" 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" color = "secondary"
/> />
</Grid> </Grid>

View File

@ -31,9 +31,9 @@ app.conf.beat_scheduler = 'django_celery_beat.schedulers:DatabaseScheduler'
# Configure the periodic tasks # Configure the periodic tasks
app.conf.beat_schedule = { app.conf.beat_schedule = {
'users-cleansing': { # Cleans abandoned users every hour 'users-cleansing': { # Cleans abandoned users every 6 hours
'task': 'users_cleansing', 'task': 'users_cleansing',
'schedule': timedelta(hours=1), 'schedule': timedelta(hours=6),
}, },
'cache-market-prices': { # Cache market prices every minutes for now. 'cache-market-prices': { # Cache market prices every minutes for now.
'task': 'cache_external_market_prices', 'task': 'cache_external_market_prices',