Skip to content

Commit

Permalink
Added back hard-deletes.
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-abraham committed Jun 11, 2021
1 parent 987b411 commit cd6ee00
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ rpcpassword=<password>
- [x] Soft-locking UTXO's (UTXO's setup on the buy/sell screen are locked to prevent use when setting up future UTXO's.)
- [x] [Optional/Default] Hard-Locking of UTXO's (UTXOs will appear gone and will be unspendable to core wallet.) This prevents accidental order invalidation
- [x] Soft-Remove Trade Order (Hide, but remember so it can be displayed if executed.)
- [x] Hard-Remove Trade Order (Invalidate the previous UTXO by using it in a transaction to yourself.) [Code exists but was removed with the bulk order changes]
- [x] Multiple RPC Connections

## TODO ##
Expand All @@ -78,7 +79,6 @@ rpcpassword=<password>
- [ ] Proxy asset signing/reissuing. (Party `A` owns admin asset, Party `B` requests a child asset be minted/reissued/etc under `A`'s admin asset.) From the creators side, this just looks like a buy order for an asset that doesn't exist yet.
- [x] Proper asset decimal/metadata support. (close enough)
- [ ] Available UTXO dialog (with option to manually lock/unlock? UTXO's.)
- [ ] Hard-Remove Trade Order (Invalidate the previous UTXO by using it in a transaction to yourself.) [Code exists but was removed with the bulk order changes]
- [x] Settings menu
- [x] -- RPC Connections
- [ ] -- Preferred rvn/asset destination address (uses address pooling currently)
Expand Down
44 changes: 44 additions & 0 deletions swap_trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,50 @@ def setup_trade(self, max_add=None):

return (True, sign_tx["hex"])

def construct_invalidate_tx(self, combine=False):
final_vin = []
final_vout = {}

#each element is {utxo}
input_utxos = [AppInstance.wallet.search_utxo(utxo) for utxo in self.order_utxos]

#Populate vins with all active UTXO's
final_vin = [utxo_copy(utxo) for utxo in input_utxos]

output_quantities = [utxo["amount"] for utxo in input_utxos]
if combine:
output_quantities = [sum(output_quantities)]

output_addresses = AppInstance.wallet.addresses.get_address_set(len(output_quantities))

#Judge all UTXO's based on the first in the list.
#TODO: Validate all are identical
trade_type = input_utxos[0]["type"]
trade_name = "rvn" if trade_type == "rvn" else (input_utxos[0]["asset"])

for index, address in enumerate(output_addresses):
quantity = output_quantities[index]
if trade_type == "rvn":
final_vout[address] = quantity
elif trade_type == "asset":
final_vout[address] = make_transfer(trade_name, quantity)

check_unlock()

new_tx = do_rpc("createrawtransaction", inputs=final_vin, outputs=final_vout)
funded_tx = do_rpc("fundrawtransaction", hexstring=new_tx, options={"changePosition": len(final_vout.keys())})
signed_raw = do_rpc("signrawtransaction", hexstring=funded_tx["hex"])["hex"]

return signed_raw

def sent_invalidate_tx(self, txid):
#Don't really need to do much except clear UTXOs and tx list
for utxo in self.order_utxos:
AppInstance.wallet.remove_lock(utxo=utxo)
self.order_utxos = []
self.transactions = []
self.order_count = 0

def missing_trades(self):
return self.order_count - len(self.order_utxos)

Expand Down
21 changes: 0 additions & 21 deletions swap_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,6 @@ def sign_partial(self):
self.raw = signed_raw["hex"]
return self.raw

def consutrct_invalidate_tx(self, new_destination=None):
self_utxo = AppInstance.wallet.search_utxo(self.utxo)
print(self_utxo)
lock_vin = [{"txid":self_utxo["utxo"]["txid"],"vout":self_utxo["utxo"]["vout"]}]
lock_vout = None
out_addr = new_destination if new_destination else self.destination

#Make sure to use local properties here in case we updated before invalidating (changed order size/amount)
if self_utxo["type"] == "rvn":
lock_vout = { out_addr: self.total_price() }
elif self_utxo["type"] == "asset": #Sell order means we need to invalide asset utxo
lock_vout = { out_addr: make_transfer(self_utxo["name"], self.in_quantity) }

check_unlock()

new_tx = do_rpc("createrawtransaction", inputs=lock_vin, outputs=lock_vout)
funded_tx = do_rpc("fundrawtransaction", hexstring=new_tx, options={"changePosition": 1})
signed_raw = do_rpc("signrawtransaction", hexstring=funded_tx["hex"])

return signed_raw

#This is run by Bob when he wants to complete an order
def complete_order(self):
final_vin = [{"txid":self.decoded["vin"]["txid"], "vout":self.decoded["vin"]["vout"], "sequence":self.decoded["vin"]["sequence"]}]
Expand Down
27 changes: 24 additions & 3 deletions ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,31 @@ def created_order(self, trade):
def action_remove_trade(self, _, confirm=True):
if self.menu_context["type"] != "trade":
return
if confirm:
if show_prompt("Remove Trade?", "Are you sure you want to remove this trade?") == QMessageBox.No:
trade = self.menu_context["data"]
if confirm and len(trade.order_utxos) > 0:
result = show_hard_delete_prompt(self)
if result == 1: # I don't know why it returns this and not YesRole :shrug:
grouped_invalidate = False
if len(trade.order_utxos) > 1: #If we have only one item, no need to ask
delete_resp = show_hard_delete_type_prompt(self)
if delete_resp not in [1, 0]:
return #anything outside of a 1/0 represents a cancel
grouped_invalidate = (delete_resp == 1)
print("Hard-Deleting Trade")
signed_tx = trade.construct_invalidate_tx(grouped_invalidate)
if signed_tx:
txid = self.preview_complete(signed_tx, "Invalidate Old Trades")
if txid:
self.wallet.add_waiting(txid)
trade.sent_invalidate_tx(txid)
self.wallet.remove_swap(trade)
elif result == 0:
print("Soft-Deleting Trade")
self.wallet.remove_swap(trade)
elif result == QMessageBox.Cancel:
return
self.wallet.remove_swap(self.menu_context["data"])
else:
self.wallet.remove_swap(trade) #simple delete
self.actionRefresh.trigger()

def action_view_trade(self, force=True):
Expand Down
33 changes: 26 additions & 7 deletions ui/ui_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from PyQt5.QtWidgets import *
from PyQt5 import uic

def show_dialog_inner(title, message, buttons, icon=QMessageBox.Information, message_extra="", parent=None):
def make_dialog(title, message, buttons, icon=QMessageBox.Information, message_extra="", parent=None):
msg = QMessageBox(parent)
msg.setIcon(icon)

Expand All @@ -13,20 +13,39 @@ def show_dialog_inner(title, message, buttons, icon=QMessageBox.Information, mes
msg.setWindowTitle(title)
msg.setStandardButtons(buttons)

return msg.exec_()
return msg

def show_error(title, message, message_extra="", parent=None):
return show_dialog_inner(title, message, QMessageBox.Ok, QMessageBox.Critical, message_extra=message_extra, parent=parent)
return make_dialog(title, message, QMessageBox.Ok, QMessageBox.Critical, message_extra=message_extra, parent=parent).exec_()

def show_dialog(title, message, message_extra="", parent=None):
return show_dialog_inner(title, message, QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Information, message_extra=message_extra, parent=parent)
return make_dialog(title, message, QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Information, message_extra=message_extra, parent=parent).exec_()

def show_prompt(title, message, message_extra="", parent=None):
return show_dialog_inner(title, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Information, message_extra=message_extra, parent=parent)
return make_dialog(title, message, QMessageBox.Yes | QMessageBox.No, QMessageBox.Information, message_extra=message_extra, parent=parent).exec_()

def show_prompt_3(title, message, message_extra="", parent=None):
return show_dialog_inner(title, message, QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Information, message_extra=message_extra, parent=parent)
return make_dialog(title, message, QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Information, message_extra=message_extra, parent=parent).exec_()


def show_number_prompt(title, message, min=1, max=1000, step=1, parent=None):
return QInputDialog.getInt(parent, title, message, min=min, max=max, step=step)
return QInputDialog.getInt(parent, title, message, min=min, max=max, step=step)

def show_hard_delete_prompt(parent=None):
hard_delete_dialog = make_dialog("Delete Trade?", "How would you like to delete the trade?", QMessageBox.Cancel, message_extra=\
"Soft-Delete: Trade is only deleted locally, signed partials already publicised can't be recalled. Use this if you plan on immediately posting an updated trade.\r\n\r\n"+
"Hard-Delete: Trade is deleted locally and UTXO's are invalidated by sending to yourself. Use this if you don't want any parties to be able to execute a previously publicised signed partial.", parent=parent)
hard_delete_dialog.addButton("Soft-Delete", QMessageBox.DestructiveRole)
hard_delete_dialog.addButton("Hard-Delete", QMessageBox.YesRole)

return hard_delete_dialog.exec_()


def show_hard_delete_type_prompt(parent=None):
hard_delete_dialog = make_dialog("Hard-Delete Method", "How would you like to transfer the assets to yourself?", QMessageBox.Cancel, message_extra=\
"Grouped: You send all aseets to a single output UTXO, smaller transaction, but likely need to set up again in the future.\r\n\r\n"+
"Identical: You send all assets in the same quantities they are now, larger transaction, but allows simple reuse of that quantity.", parent=parent)
hard_delete_dialog.addButton("Identical", QMessageBox.DestructiveRole)
hard_delete_dialog.addButton("Grouped", QMessageBox.YesRole)

return hard_delete_dialog.exec_()
6 changes: 3 additions & 3 deletions wallet_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,15 +451,16 @@ def swap_utxo_spent(self, utxo, in_mempool=True, check_cache=True):
txout = do_rpc("gettxout", txid=txid, n=vout, include_mempool=in_mempool)
return txout == None

#return ({type, utxo}, amount)
def search_utxo(self, utxo):
(txid, vout) = split_utxo(utxo)
for utxo in self.utxos:
if utxo["txid"] == txid and utxo["vout"] == vout:
return {"type": "rvn", "utxo": utxo}
return utxo
for asset_name in self.my_asset_names:
for a_utxo in self.assets[asset_name]["outpoints"]:
if a_utxo["txid"] == txid and a_utxo["vout"] == vout:
return {"type": "asset", "utxo": a_utxo, "name": asset_name}
return a_utxo
return None

def is_locked(self, utxo):
Expand Down Expand Up @@ -555,7 +556,6 @@ def fund_transaction_final(fn_rpc, send_rvn, recv_rvn, target_addr, vins, vouts,

return True


from rvn_rpc import *
from app_instance import AppInstance
from wallet_addresses import WalletAddresses
Expand Down

0 comments on commit cd6ee00

Please sign in to comment.