Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coinchooser: make output value rounding configurable #3878

Merged
merged 1 commit into from
Feb 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,8 @@ def setAmount(self, byte_size):
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)

Expand Down Expand Up @@ -2893,6 +2894,18 @@ def on_unconf(x):
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()
Expand Down
20 changes: 16 additions & 4 deletions lib/coinchooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def strip_unneeded(bkts, sufficient_funds):

class CoinChooserBase(PrintError):

enable_output_value_rounding = False

def keys(self, coins):
raise NotImplementedError

Expand Down Expand Up @@ -135,7 +137,13 @@ def trailing_zeroes(val):
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
Expand All @@ -150,8 +158,10 @@ def trailing_zeroes(val):
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)

Expand Down Expand Up @@ -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