Implement collaborative cancellation

This commit is contained in:
Reckless_Satoshi 2022-01-23 11:02:25 -08:00
parent 6377b052ce
commit b934534e1e
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
5 changed files with 180 additions and 103 deletions

View File

@ -421,14 +421,46 @@ class Logics():
return True, None
# 5) When trade collateral has been posted (after escrow)
'''Always goes to cancelled status. Collaboration is needed.
When a user asks for cancel, 'order.is_pending_cancel' goes True.
'''Always goes to CCA status. Collaboration is needed.
When a user asks for cancel, 'order.m/t/aker_asked_cancel' goes True.
When the second user asks for cancel. Order is totally cancelled.
Has a small cost for both parties to prevent node DDOS.'''
Must have a small cost for both parties to prevent node DDOS.'''
elif order.status in [Order.Status.WFI, Order.Status.CHA, Order.Status.FSE]:
# if the maker had asked, and now the taker does: cancel order, return everything
if order.maker_asked_cancel and user == order.taker:
cls.collaborative_cancel(order)
return True, None
# if the taker had asked, and now the maker does: cancel order, return everything
elif order.taker_asked_cancel and user == order.maker:
cls.collaborative_cancel(order)
return True, None
# Otherwise just make true the asked for cancel flags
elif user == order.taker:
order.taker_asked_cancel = True
order.save()
return True, None
elif user == order.maker:
order.maker_asked_cancel = True
order.save()
return True, None
else:
return False, {'bad_request':'You cannot cancel this order'}
@classmethod
def collaborative_cancel(cls, order):
cls.return_bond(order.maker_bond)
cls.return_bond(order.taker_bond)
cls.return_escrow(order)
order.status = Order.Status.CCA
order.save()
return
def publish_order(order):
order.status = Order.Status.PUB
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])

View File

@ -142,7 +142,8 @@ class Order(models.Model):
# order participants
maker = models.ForeignKey(User, related_name='maker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a maker can only make one order
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None, blank=True) # unique = True, a taker can only take one order
is_pending_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
maker_asked_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
taker_asked_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
is_fiat_sent = models.BooleanField(default=False, null=False)
# in dispute

View File

@ -189,7 +189,6 @@ class OrderView(viewsets.ViewSet):
# 7 a. ) If seller and status is 'WF2' or 'WFE'
elif data['is_seller'] and (order.status == Order.Status.WF2 or order.status == Order.Status.WFE):
# If the two bonds are locked, reply with an ESCROW hold invoice.
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
valid, context = Logics.gen_escrow_hold_invoice(order, request.user)
@ -210,15 +209,23 @@ class OrderView(viewsets.ViewSet):
return Response(context, status.HTTP_400_BAD_REQUEST)
# 8) If status is 'CHA' or 'FSE' and all HTLCS are in LOCKED
elif order.status == Order.Status.CHA or order.status == Order.Status.FSE: # TODO Add the other status
elif order.status in [Order.Status.WFI, Order.Status.CHA, Order.Status.FSE]:
print('CCCAAABBAAAAAAAAAAAAAAAA')
# If all bonds are locked.
if order.maker_bond.status == order.taker_bond.status == order.trade_escrow.status == LNPayment.Status.LOCKED:
# add whether a collaborative cancel is pending
data['pending_cancel'] = order.is_pending_cancel
print('AAABBAAAAAAAAAAAAAAAA')
# add whether a collaborative cancel is pending or has been asked
if (data['is_maker'] and order.taker_asked_cancel) or (data['is_taker'] and order.maker_asked_cancel):
print('PENDING')
data['pending_cancel'] = True
elif (data['is_maker'] and order.maker_asked_cancel) or (data['is_taker'] and order.taker_asked_cancel):
print('ASKED')
data['asked_for_cancel'] = True
else:
data['asked_for_cancel'] = False
# 9) If status is 'DIS' and all HTLCS are in LOCKED
elif order.status == Order.Status.DIS:# TODO Add the other status
elif order.status == Order.Status.DIS:
# add whether the dispute statement has been received
if data['is_maker']:

View File

@ -9,9 +9,7 @@ import AccessTimeIcon from '@mui/icons-material/AccessTime';
import NumbersIcon from '@mui/icons-material/Numbers';
import PriceChangeIcon from '@mui/icons-material/PriceChange';
import PaymentsIcon from '@mui/icons-material/Payments';
import MoneyIcon from '@mui/icons-material/Money';
import ArticleIcon from '@mui/icons-material/Article';
import ContentCopy from "@mui/icons-material/ContentCopy";
function getCookie(name) {
let cookieValue = null;
@ -45,12 +43,13 @@ export default class OrderPage extends Component {
total_secs_exp: 300,
loading: true,
openCancel: false,
openCollaborativeCancel: false,
};
this.orderId = this.props.match.params.orderId;
this.getCurrencyDict();
this.getOrderDetails();
// Refresh delais according to Order status
// Refresh delays according to Order status
this.statusToDelay = {
"0": 3000, //'Waiting for maker bond'
"1": 30000, //'Public'
@ -73,60 +72,16 @@ export default class OrderPage extends Component {
}
}
// Unneeded for the most part. Let's keep variable names as they come from the API
// Will need some renaming everywhere, but will decrease the mess.
completeSetState=(newStateVars)=>{
console.log(newStateVars)
var otherStateVars = {
loading: false,
delay: this.setDelay(newStateVars.status),
currencyCode: this.getCurrencyCode(newStateVars.currency),
};
console.log(otherStateVars)
var completeStateVars = Object.assign({}, newStateVars, otherStateVars);
console.log(completeStateVars)
this.setState(completeStateVars);
// {
// loading: false,
// delay: this.setDelay(data.status),
// id: data.id,
// status: data.status,
// status_message: data.status_message,
// type: data.type,
// currency: data.currency,
// currencyCode: this.getCurrencyCode(data.currency),
// amount: data.amount,
// payment_method: data.payment_method,
// isExplicit: data.is_explicit,
// premium: data.premium,
// satoshis: data.satoshis,
// makerId: data.maker,
// is_participant: data.is_participant,
// urNick: data.ur_nick,
// maker_nick: data.maker_nick,
// takerId: data.taker,
// taker_nick: data.taker_nick,
// is_maker: data.is_maker,
// is_taker: data.is_taker,
// is_buyer: data.is_buyer,
// is_seller: data.is_seller,
// penalty: data.penalty,
// expires_at: data.expires_at,
// bad_request: data.bad_request,
// bond_invoice: data.bond_invoice,
// bondSatoshis: data.bond_satoshis,
// escrow_invoice: data.escrow_invoice,
// escrowSatoshis: data.escrow_satoshis,
// invoice_amount: data.invoice_amount,
// total_secs_exp: data.total_secs_exp,
// num_similar_orders: data.num_similar_orders,
// price_now: data.price_now,
// premium_now: data.premium_now,
// probots_in_book: data.robots_in_book,
// premium_percentile: data.premium_percentile,
// num_similar_orders: data.num_similar_orders
// })
}
getOrderDetails() {
this.setState(null)
fetch('/api/order' + '?order_id=' + this.orderId)
@ -277,6 +232,53 @@ export default class OrderPage extends Component {
)
}
handleClickConfirmCollaborativeCancelButton=()=>{
console.log(this.state)
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'cancel',
}),
};
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
.then((response) => response.json())
.then((data) => (console.log(data) & this.getOrderDetails(data.id)));
this.handleClickCloseCollaborativeCancelDialog();
}
handleClickOpenCollaborativeCancelDialog = () => {
this.setState({openCollaborativeCancel: true});
};
handleClickCloseCollaborativeCancelDialog = () => {
this.setState({openCollaborativeCancel: false});
};
CollaborativeCancelDialog =() =>{
return(
<Dialog
open={this.state.openCollaborativeCancel}
onClose={this.handleClickCloseCollaborativeCancelDialog}
aria-labelledby="collaborative-cancel-dialog-title"
aria-describedby="collaborative-cancel-dialog-description"
>
<DialogTitle id="cancel-dialog-title">
{"Collaborative cancel the order?"}
</DialogTitle>
<DialogContent>
<DialogContentText id="cancel-dialog-description">
The trade escrow has been posted. The order can be cancelled only if both, maker and
taker, agree to cancel.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseCollaborativeCancelDialog} autoFocus>Go back</Button>
<Button onClick={this.handleClickConfirmCollaborativeCancelButton}> Collaborative Cancel </Button>
</DialogActions>
</Dialog>
)
}
CancelButton = () => {
// If maker and Waiting for Bond. Or if taker and Waiting for bond.
@ -289,15 +291,25 @@ export default class OrderPage extends Component {
)}
// If the order does not yet have an escrow deposited. Show dialog
// to confirm forfeiting the bond
if (this.state.status in [0,1,3,6,7]){
if ([1,3,6,7].includes(this.state.status)){
return(
<div id="openDialogCancelButton">
<Grid item xs={12} align="center">
<this.CancelDialog/>
<Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>Cancel</Button>
</Grid>
</div>
)}
// TODO If the escrow is Locked, show the collaborative cancel button.
// If the escrow is Locked, show the collaborative cancel button.
if ([8,9].includes(this.state.status)){
return(
<Grid item xs={12} align="center">
<this.CollaborativeCancelDialog/>
<Button variant='contained' color='secondary' onClick={this.handleClickOpenCollaborativeCancelDialog}>Collaborative Cancel</Button>
</Grid>
)}
// If none of the above do not return a cancel button.
return(null)
@ -414,23 +426,48 @@ export default class OrderPage extends Component {
</>
: null}
{/* If the counterparty asked for collaborative cancel */}
{this.state.pending_cancel ?
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
{this.state.is_maker ? this.state.taker_nick : this.state.maker_nick} is asking for a collaborative cancel
</Alert>
</Grid>
</>
: null}
{/* If the user has asked for a collaborative cancel */}
{this.state.asked_for_cancel ?
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
You asked for a collaborative cancellation
</Alert>
</Grid>
</>
: null}
</Paper>
</Grid>
<Grid item xs={12} align="center">
{/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */}
{this.state.is_participant ?
<this.CancelButton/>
:
<>
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Button variant='contained' color='primary' onClick={this.handleClickTakeOrderButton}>Take Order</Button>
</Grid>
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>
</Grid>
</>
</Grid>
}
</Grid>
</Grid>
)
}

View File

@ -89,15 +89,15 @@ export default class TradeBox extends Component {
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The RoboSats staff will examine the statements and evidence provided by the participants.
It is best if you provide a burner contact method on your statement for the staff to contact you.
The satoshis in the trade escrow will be sent to the dispute winner, while the dispute
loser will lose the bond.
The RoboSats staff will examine the statements and evidence provided. You need to build
a complete case, as the staff cannot read the chat. It is best to provide a burner contact
method with your statement. The satoshis in the trade escrow will be sent to the dispute winner,
while the dispute loser will lose the bond.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseConfirmDispute} autoFocus>Disagree</Button>
<Button onClick={this.handleClickAgreeDisputeButton}> Agree </Button>
<Button onClick={this.handleClickAgreeDisputeButton}> Agree and open dispute </Button>
</DialogActions>
</Dialog>
)