commit cccf3807538639fb638424b06faf0563e7b7d80e
parent 95c5815fe3c922807af06547051339b082bcb926
Author: ThomasV <thomasv@electrum.org>
Date: Fri, 9 Feb 2018 12:13:26 +0100
Merge pull request #3878 from SomberNight/coinchooser_output_rounding
coinchooser: make output value rounding configurable
Diffstat:
2 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -1136,7 +1136,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def feerounding_onclick():
text = (self.feerounding_text + '\n\n' +
_('To somewhat protect your privacy, Electrum tries to create change with similar precision to other outputs.') + ' ' +
- _('At most 100 satoshis might be lost due to this rounding.') + '\n' +
+ _('At most 100 satoshis might be lost due to this rounding.') + ' ' +
+ _("You can disable this setting in '{}'.").format(_('Preferences')) + '\n' +
_('Also, dust is not kept as change, but added to the fee.'))
QMessageBox.information(self, 'Fee rounding', text)
@@ -2893,6 +2894,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
unconf_cb.stateChanged.connect(on_unconf)
tx_widgets.append((unconf_cb, None))
+ def on_outrounding(x):
+ self.config.set_key('coin_chooser_output_rounding', bool(x))
+ enable_outrounding = self.config.get('coin_chooser_output_rounding', False)
+ outrounding_cb = QCheckBox(_('Enable output value rounding'))
+ outrounding_cb.setToolTip(
+ _('Set the value of the change output so that it has similar precision to the other outputs.') + '\n' +
+ _('This might improve your privacy somewhat.') + '\n' +
+ _('If enabled, at most 100 satoshis might be lost due to this, per transaction.'))
+ outrounding_cb.setChecked(enable_outrounding)
+ outrounding_cb.stateChanged.connect(on_outrounding)
+ tx_widgets.append((outrounding_cb, None))
+
# Fiat Currency
hist_checkbox = QCheckBox()
fiat_address_checkbox = QCheckBox()
diff --git a/lib/coinchooser.py b/lib/coinchooser.py
@@ -87,6 +87,8 @@ def strip_unneeded(bkts, sufficient_funds):
class CoinChooserBase(PrintError):
+ enable_output_value_rounding = False
+
def keys(self, coins):
raise NotImplementedError
@@ -135,7 +137,13 @@ class CoinChooserBase(PrintError):
zeroes = [trailing_zeroes(i) for i in output_amounts]
min_zeroes = min(zeroes)
max_zeroes = max(zeroes)
- zeroes = range(max(0, min_zeroes - 1), (max_zeroes + 1) + 1)
+
+ if n > 1:
+ zeroes = range(max(0, min_zeroes - 1), (max_zeroes + 1) + 1)
+ else:
+ # if there is only one change output, this will ensure that we aim
+ # to have one that is exactly as precise as the most precise output
+ zeroes = [min_zeroes]
# Calculate change; randomize it a bit if using more than 1 output
remaining = change_amount
@@ -150,8 +158,10 @@ class CoinChooserBase(PrintError):
n -= 1
# Last change output. Round down to maximum precision but lose
- # no more than 100 satoshis to fees (2dp)
- N = pow(10, min(2, zeroes[0]))
+ # no more than 10**max_dp_to_round_for_privacy
+ # e.g. a max of 2 decimal places means losing 100 satoshis to fees
+ max_dp_to_round_for_privacy = 2 if self.enable_output_value_rounding else 0
+ N = pow(10, min(max_dp_to_round_for_privacy, zeroes[0]))
amount = (remaining // N) * N
amounts.append(amount)
@@ -370,4 +380,6 @@ def get_name(config):
def get_coin_chooser(config):
klass = COIN_CHOOSERS[get_name(config)]
- return klass()
+ coinchooser = klass()
+ coinchooser.enable_output_value_rounding = config.get('coin_chooser_output_rounding', False)
+ return coinchooser