Fix onchain payouts bug

This commit is contained in:
Reckless_Satoshi 2022-10-22 07:23:22 -07:00
parent aa445418d5
commit 5723cde20e
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
5 changed files with 82 additions and 36 deletions

View File

@ -16,6 +16,8 @@ from . import lightning_pb2_grpc as lightningstub
from . import router_pb2 as routerrpc
from . import router_pb2_grpc as routerstub
import time, random
#######
# Should work with LND (c-lightning in the future if there are features that deserve the work)
#######
@ -127,7 +129,7 @@ class LNNode:
}
@classmethod
def pay_onchain(cls, onchainpayment):
def pay_onchain(cls, onchainpayment, valid_code=1, on_mempool_code=2):
"""Send onchain transaction for buyer payouts"""
if config("DISABLE_ONCHAIN", cast=bool):
@ -141,14 +143,24 @@ class LNNode:
spend_unconfirmed=True,
)
response = cls.lightningstub.SendCoins(
request, metadata=[("macaroon", MACAROON.hex())]
)
# Cheap security measure to ensure there has been some non-deterministic time between request and DB check
time.sleep(random.uniform(0.5, 10))
onchainpayment.txid = response.txid
onchainpayment.save()
if onchainpayment.status == valid_code:
# Changing the state to "MEMPO" should be atomic with SendCoins.
onchainpayment.status = on_mempool_code
onchainpayment.save()
response = cls.lightningstub.SendCoins(
request, metadata=[("macaroon", MACAROON.hex())]
)
return True
onchainpayment.txid = response.txid
onchainpayment.save()
return True
elif onchainpayment.status == on_mempool_code:
# Bug, double payment attempted
return True
@classmethod
def cancel_return_hold_invoice(cls, payment_hash):

View File

@ -734,7 +734,11 @@ class Logics:
if not order.taker_bond:
return False, {"bad_request": "Wait for your order to be taken."}
if (
not ( order.taker_bond.status == order.maker_bond.status == LNPayment.Status.LOCKED)
not (
order.taker_bond.status
== order.maker_bond.status
== LNPayment.Status.LOCKED
)
and not order.status == Order.Status.FAI
):
return False, {
@ -753,7 +757,7 @@ class Logics:
payout = LNNode.validate_ln_invoice(invoice, num_satoshis)
if not payout["valid"]:
return False, payout['context']
return False, payout["context"]
order.payout, _ = LNPayment.objects.update_or_create(
concept=LNPayment.Concepts.PAYBUYER,
@ -763,10 +767,10 @@ class Logics:
receiver=user,
# if there is a LNPayment matching these above, it updates that one with defaults below.
defaults={
"invoice":invoice,
"status":LNPayment.Status.VALIDI,
"num_satoshis":num_satoshis,
"description": payout['description'],
"invoice": invoice,
"status": LNPayment.Status.VALIDI,
"num_satoshis": num_satoshis,
"description": payout["description"],
"payment_hash": payout["payment_hash"],
"created_at": payout["created_at"],
"expires_at": payout["expires_at"],
@ -1429,10 +1433,12 @@ class Logics:
if not order.payout_tx.status == OnchainPayment.Status.VALID:
return False
valid = LNNode.pay_onchain(order.payout_tx)
valid = LNNode.pay_onchain(
order.payout_tx,
valid_code=OnchainPayment.Status.VALID,
on_mempool_code=OnchainPayment.Status.MEMPO,
)
if valid:
order.payout_tx.status = OnchainPayment.Status.MEMPO
order.payout_tx.save()
order.status = Order.Status.SUC
order.save()
send_message.delay(order.id, "trade_successful")

View File

@ -6,7 +6,6 @@ import {
ToggleButtonGroup,
ToggleButton,
IconButton,
Box,
Link,
Paper,
Rating,
@ -27,6 +26,7 @@ import {
DialogContentText,
DialogTitle,
} from '@mui/material';
import { LoadingButton } from '@mui/lab';
import QRCode from 'react-qr-code';
import Countdown, { zeroPad } from 'react-countdown';
import Chat from './EncryptedChat';
@ -61,6 +61,10 @@ class TradeBox extends Component {
super(props);
this.state = {
openConfirmFiatReceived: false,
loadingButtonFiatSent: false,
loadingButtonFiatReceived: false,
loadingSubmitInvoice: false,
loadingSubmitAddress: false,
openConfirmDispute: false,
receiveTab: 0,
address: '',
@ -232,7 +236,15 @@ class TradeBox extends Component {
<Button onClick={this.handleClickCloseConfirmFiatReceived} autoFocus>
{t('Go back')}
</Button>
<Button onClick={this.handleClickTotallyConfirmFiatReceived}>{t('Confirm')}</Button>
<LoadingButton
loading={this.state.loadingButtonFiatReceived}
onClick={() => {
this.setState({ loadingButtonFiatReceived: true });
this.handleClickTotallyConfirmFiatReceived();
}}
>
{t('Confirm')}
</LoadingButton>
</DialogActions>
</Dialog>
);
@ -627,7 +639,7 @@ class TradeBox extends Component {
};
handleClickSubmitInvoiceButton = () => {
this.setState({ badInvoice: false });
this.setState({ badInvoice: false, loadingSubmitInvoice: true });
apiClient
.post('/api/order/?order_id=' + this.props.data.id, {
@ -636,7 +648,8 @@ class TradeBox extends Component {
})
.then(
(data) =>
this.setState({ badInvoice: data.bad_invoice }) & this.props.completeSetState(data),
this.setState({ badInvoice: data.bad_invoice, loadingSubmitInvoice: false }) &
this.props.completeSetState(data),
);
};
@ -659,7 +672,7 @@ class TradeBox extends Component {
};
handleClickSubmitAddressButton = () => {
this.setState({ badInvoice: false });
this.setState({ badInvoice: false, loadingSubmitAddress: true });
apiClient
.post('/api/order/?order_id=' + this.props.data.id, {
@ -669,7 +682,8 @@ class TradeBox extends Component {
})
.then(
(data) =>
this.setState({ badAddress: data.bad_address }) & this.props.completeSetState(data),
this.setState({ badAddress: data.bad_address, loadingSubmitAddress: false }) &
this.props.completeSetState(data),
);
};
@ -838,13 +852,14 @@ class TradeBox extends Component {
/>
</Grid>
<Grid item xs={12} align='center'>
<Button
<LoadingButton
loading={this.state.loadingSubmitInvoice}
onClick={this.handleClickSubmitInvoiceButton}
variant='contained'
color='primary'
>
{t('Submit')}
</Button>
</LoadingButton>
</Grid>
</Grid>
</div>
@ -940,13 +955,14 @@ class TradeBox extends Component {
<div style={{ height: 10 }} />
<Grid item xs={12} align='center'>
<Button
<LoadingButton
loading={this.state.loadingSubmitAddress}
onClick={this.handleClickSubmitAddressButton}
variant='contained'
color='primary'
>
{t('Submit')}
</Button>
</LoadingButton>
</Grid>
</div>
<List>
@ -1192,7 +1208,10 @@ class TradeBox extends Component {
.post('/api/order/?order_id=' + this.props.data.id, {
action: 'confirm',
})
.then((data) => this.props.completeSetState(data));
.then((data) => {
this.props.completeSetState(data),
this.setState({ loadingButtonFiatSent: false, loadingButtonFiatReceived: false });
});
};
handleRatingUserChange = (e) => {
@ -1224,10 +1243,14 @@ class TradeBox extends Component {
<Grid container spacing={1}>
<Grid item xs={12} align='center'>
<Button
loading={this.state.loadingButtonFiatSent}
defaultValue='confirm'
variant='contained'
color='secondary'
onClick={this.handleClickConfirmButton}
onClick={() => {
this.setState({ loadingButtonFiatSent: true });
this.handleClickConfirmButton();
}}
>
{t('Confirm {{amount}} {{currencyCode}} sent', {
currencyCode: this.props.data.currencyCode,
@ -1249,7 +1272,8 @@ class TradeBox extends Component {
const { t } = this.props;
return (
<Grid item xs={12} align='center'>
<Button
<LoadingButton
loading={this.state.loadingButtonFiatReceived}
defaultValue='confirm'
variant='contained'
color='secondary'
@ -1265,7 +1289,7 @@ class TradeBox extends Component {
),
),
})}
</Button>
</LoadingButton>
</Grid>
);
}
@ -1732,13 +1756,14 @@ class TradeBox extends Component {
/>
</Grid>
<Grid item xs={12} align='center'>
<Button
<LoadingButton
loading={this.state.loadingSubmitInvoice}
onClick={this.handleClickSubmitInvoiceButton}
variant='contained'
color='primary'
>
Submit
</Button>
{t('Submit')}
</LoadingButton>
</Grid>
{this.showBondIsReturned()}
</Grid>

View File

@ -84,9 +84,11 @@ class UnsafeAlert extends Component {
>
<AlertTitle>{t('You are not using RoboSats privately')}</AlertTitle>
<Trans i18nKey='desktop_unsafe_alert'>
<a>Some features are disabled for your protection (e.g. chat) and you will not be
able to complete a trade without them. To protect your privacy and fully enable
RoboSats, use </a>
<a>
Some features are disabled for your protection (e.g. chat) and you will not be
able to complete a trade without them. To protect your privacy and fully enable
RoboSats, use{' '}
</a>
<Link href='https://www.torproject.org/download/' target='_blank'>
Tor Browser
</Link>

View File

@ -8,6 +8,7 @@ def basic(request, *args, **kwargs):
context = {"ONION_LOCATION": config("ONION_LOCATION")}
return render(request, "frontend/basic.html", context=context)
def pro(request, *args, **kwargs):
context = {"ONION_LOCATION": config("ONION_LOCATION")}
return render(request, "frontend/pro.html", context=context)