diff --git a/electrum/gui/qt/confirm_tx_dialog.py b/electrum/gui/qt/confirm_tx_dialog.py index dfd7a889c209..d75c51934c05 100644 --- a/electrum/gui/qt/confirm_tx_dialog.py +++ b/electrum/gui/qt/confirm_tx_dialog.py @@ -414,6 +414,7 @@ def add_cv_action(configvar: 'ConfigVarWithConfig', action: Callable[[], None]): add_cv_action(self.config.cv.WALLET_MERGE_DUPLICATE_OUTPUTS, self.toggle_merge_duplicate_outputs) add_cv_action(self.config.cv.WALLET_SPEND_CONFIRMED_ONLY, self.toggle_confirmed_only) add_cv_action(self.config.cv.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING, self.toggle_output_rounding) + add_cv_action(self.config.cv.WALLET_SORT_TX_OUTPUTS, self.toggle_sort_outputs) self.pref_button = QToolButton() self.pref_button.setIcon(read_QIcon("preferences.png")) self.pref_button.setMenu(self.pref_menu) @@ -467,6 +468,11 @@ def toggle_confirmed_only(self): self.config.WALLET_SPEND_CONFIRMED_ONLY = b self.trigger_update() + def toggle_sort_outputs(self): + b = not self.config.WALLET_SORT_TX_OUTPUTS + self.config.WALLET_SORT_TX_OUTPUTS = b + self.trigger_update() + def toggle_io_visibility(self): b = not self.config.GUI_QT_TX_EDITOR_SHOW_IO self.config.GUI_QT_TX_EDITOR_SHOW_IO = b diff --git a/electrum/simple_config.py b/electrum/simple_config.py index 1c1d17d3f3c6..85220ff8d7c5 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py @@ -991,6 +991,18 @@ def _default_swapserver_url(self) -> str: short_desc=lambda: _('Send change to Lightning'), long_desc=lambda: _('If possible, send the change of this transaction to your channels, with a submarine swap'), ) + # TODO: once randomizing transaction outputs is implemented, replace the language here to randomize + WALLET_SORT_TX_OUTPUTS = ConfigVar( + 'sort_tx_outputs', default=True, type_=bool, + short_desc=lambda: _('Sort transaction outputs (BIP-69)'), + long_desc=lambda: _("""Transaction outputs can be arbitrarily sorted, and transfers are still going to work. + +This means that from the ordering, one could deduce the client that was used to generate the transaction. + +By having a normalized order for the outputs of a transaction, the BIP-69 standard makes this fingerprinting impossible. + +This option is enabled by default, as it increases privacy, and it only should be turned off, if the generated transaction has to have a specific output ordering."""), + ) FX_USE_EXCHANGE_RATE = ConfigVar('use_exchange_rate', default=False, type_=bool) FX_CURRENCY = ConfigVar('currency', default='EUR', type_=str) diff --git a/electrum/wallet.py b/electrum/wallet.py index ed146b7b23ba..18a735d089fc 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -1933,7 +1933,7 @@ def fee_estimator(size: Union[int, float, Decimal]) -> int: (x,i) = i_max[-1] outputs[i].value += (amount - distr_amount) - tx = PartialTransaction.from_io(list(coins), list(outputs)) + tx = PartialTransaction.from_io(list(coins), list(outputs), BIP69_sort = self.config.WALLET_SORT_TX_OUTPUTS) # Timelock tx to current height. tx.locktime = get_locktime_for_new_transaction(self.network)