Browse Source

Remove amountpower and use uniform distn instead

Remove the power law generation for amount_fractions, instead use
a uniform distribution

This is part of the 2/2019 Plan to improve the privacy of JoinMarket's
tumbler script:
https://gist.github.com/chris-belcher/7e92810f07328fdfdef2ce444aad0968
master
chris-belcher 6 years ago
parent
commit
b79d34a348
No known key found for this signature in database
GPG Key ID: EF734EA677F31129
  1. 24
      jmclient/jmclient/schedule.py
  2. 1
      jmclient/test/test_schedule.py
  3. 7
      scripts/cli_options.py
  4. 44
      scripts/qtsupport.py

24
jmclient/jmclient/schedule.py

@ -7,7 +7,7 @@ import random
import sys
from .configure import validate_address, jm_single
from .support import rand_exp_array, rand_norm_array, rand_pow_array, rand_weighted_choice
from .support import rand_exp_array, rand_norm_array, rand_weighted_choice
"""Utility functions for dealing with Taker schedules.
- get_schedule(filename):
@ -60,9 +60,8 @@ def get_schedule(filename):
waittime, rounding, completed])
return (True, schedule)
def get_amount_fractions(power, count):
"""Get 'count' fractions following power law distn according to
parameter 'power'
def get_amount_fractions(count):
"""Get 'count' fractions following uniform distn
Note that this function is not entirely generic; it ensures that
the final entry is larger than a certain fraction, for a reason
specific to the way the tumbler algo works: the last entry
@ -71,9 +70,16 @@ def get_amount_fractions(power, count):
setting, so we make sure it's appreciable to begin with.
"""
while True:
amount_fractions = rand_pow_array(power, count)
amount_fractions = [1.0 - x for x in amount_fractions]
y = [x / sum(amount_fractions) for x in amount_fractions]
knives = [random.random() for i in range(count-1)]
knives = sorted(knives)[::-1]
y = []
l = 1
k = 1
for k in knives:
y.append( l - k )
l = k
y.append(k)
#Here we insist that the last entry in the list is more
#than 5% of the total, to account for tweaks upwards
#on previous joins.
@ -134,7 +140,7 @@ def get_tumble_schedule(options, destaddrs, mixdepth_balance_dict):
# amount_fraction cant be 1.0, some coins must be left over
if txcount == 1:
txcount = 2
amount_fractions = get_amount_fractions(options['amountpower'], txcount)
amount_fractions = get_amount_fractions(txcount)
# transaction times are uncorrelated
# time between events in a poisson process followed exp
waits = rand_exp_array(options['timelambda'], txcount)
@ -232,7 +238,7 @@ def tweak_tumble_schedule(options, schedule, last_completed, destaddrs=[]):
alreadyspent = sum([x[1] for x in already_done])
tobespent = 1.0 - alreadyspent
#power law for what's left:
new_fracs = get_amount_fractions(options['amountpower'], len(tobedone))
new_fracs = get_amount_fractions(len(tobedone))
#rescale; the sum must be 'tobespent':
new_fracs = [x*tobespent for x in new_fracs]
#starting from the known 'last_completed+1' index, apply these new

1
jmclient/test/test_schedule.py

@ -65,7 +65,6 @@ def get_options():
options.txfee = 5000
options.addrcount = 3
options.mintxcount = 1
options.amountpower = 100
options.timelambda = 0.2
options.waittime = 10
options.stage1_timelambda_increase = 3

7
scripts/cli_options.py

@ -310,13 +310,6 @@ def get_tumbler_parser():
default=0,
help=
'percent of funds to donate to joinmarket development, or zero to opt out (default=0%)')
parser.add_option(
'--amountpower',
type='float',
dest='amountpower',
default=100.0,
help=
'The output amounts follow a power law distribution, this is the power, default=100.0')
parser.add_option(
'-l',
'--timelambda',

44
scripts/qtsupport.py

@ -626,7 +626,6 @@ class SchFinishPage(QWizardPage):
results = []
sN = ['Makercount sdev',
'Tx count sdev',
'Amount power',
'Minimum maker count',
'Minimum transaction count',
'Min coinjoin amount',
@ -640,7 +639,6 @@ class SchFinishPage(QWizardPage):
"transaction.",
"Standard deviation of the number of transactions to use in each "
"mixdepth",
"A parameter to control the random coinjoin sizes.",
"The lowest allowed number of maker counterparties.",
"The lowest allowed number of transactions in one mixdepth.",
"The lowest allowed size of any coinjoin, in satoshis.",
@ -652,12 +650,12 @@ class SchFinishPage(QWizardPage):
"is the relative probability of rounding to " + w +
" significant figures"]
#types
sT = [float, float, float, int, int, int, float, float, float] + [int]*5
sT = [float, float, int, int, int, float, float, float] + [int]*5
#constraints
sMM = [(0.0, 10.0, 2), (0.0, 10.0, 2), (1.0, 10000.0, 1), (2,20),
sMM = [(0.0, 10.0, 2), (0.0, 10.0, 2), (2,20),
(1, 10), (100000, 100000000), (10.0, 500.0, 2), (0, 100, 1),
(0.0, 1.0, 3)] + [(0, 10000)]*5
sD = ['1.0', '1.0', '100.0', '2', '1', '1000000', '20', '3', '0.25'] +\
sD = ['1.0', '1.0', '2', '1', '1000000', '20', '3', '0.25'] +\
['55', '15', '25', '65', '40']
for x in zip(sN, sH, sT, sD, sMM):
ql = QLabel(x[0])
@ -677,15 +675,14 @@ class SchFinishPage(QWizardPage):
#fields not considered 'mandatory' as defaults are accepted
self.registerField("makercountsdev", results[0][1])
self.registerField("txcountsdev", results[1][1])
self.registerField("amountpower", results[2][1])
self.registerField("minmakercount", results[3][1])
self.registerField("mintxcount", results[4][1])
self.registerField("mincjamount", results[5][1])
self.registerField("waittime", results[6][1])
self.registerField("stage1_timelambda_increase", results[7][1])
self.registerField("rounding_chance", results[8][1])
self.registerField("minmakercount", results[2][1])
self.registerField("mintxcount", results[3][1])
self.registerField("mincjamount", results[4][1])
self.registerField("waittime", results[5][1])
self.registerField("stage1_timelambda_increase", results[6][1])
self.registerField("rounding_chance", results[7][1])
for i in range(5):
self.registerField("rounding_sigfig_weight_" + str(i+1), results[9+i][1])
self.registerField("rounding_sigfig_weight_" + str(i+1), results[8+i][1])
class SchIntroPage(QWizardPage):
def __init__(self, parent):
@ -753,7 +750,6 @@ class ScheduleWizard(QWizard):
self.opts['txcountparams'] = (int(self.field("txcountparams")),
float(self.field("txcountsdev")))
self.opts['mintxcount'] = int(self.field("mintxcount"))
self.opts['amountpower'] = float(self.field("amountpower"))
self.opts['timelambda'] = float(self.field("timelambda"))
self.opts['waittime'] = float(self.field("waittime"))
self.opts["stage1_timelambda_increase"] = float(self.field("stage1_timelambda_increase"))
@ -776,7 +772,6 @@ class TumbleRestartWizard(QWizard):
def getOptions(self):
self.opts = {}
self.opts['amountpower'] = float(self.field("amountpower"))
self.opts['mincjamount'] = int(self.field("mincjamount"))
relfeeval = float(self.field("maxrelfee"))
absfeeval = int(self.field("maxabsfee"))
@ -795,21 +790,19 @@ class RestartSettingsPage(QWizardPage):
layout.setSpacing(4)
results = []
sN = ['Amount power',
'Min coinjoin amount',
sN = ['Min coinjoin amount',
'Max relative fee per counterparty (e.g. 0.005)',
'Max fee per counterparty, satoshis (e.g. 10000)']
#Tooltips
sH = ["A parameter to control the random coinjoin sizes.",
"The lowest allowed size of any coinjoin, in satoshis.",
sH = ["The lowest allowed size of any coinjoin, in satoshis.",
"A decimal fraction (e.g. 0.001 = 0.1%) (this AND next must be violated to reject",
"Integer number of satoshis (this AND previous must be violated to reject)"]
#types
sT = [float, int, float, int]
sT = [int, float, int]
#constraints
sMM = [(1.0, 10000.0, 1), (100000, 100000000), (0.000001, 0.25, 6),
sMM = [(100000, 100000000), (0.000001, 0.25, 6),
(0, 10000000)]
sD = ['100.0', '1000000', '0.0005', '10000']
sD = ['1000000', '0.0005', '10000']
for x in zip(sN, sH, sT, sD, sMM):
ql = QLabel(x[0])
ql.setToolTip(x[1])
@ -826,7 +819,6 @@ class RestartSettingsPage(QWizardPage):
layout.addWidget(x[1], i + 1, 1, 1, 2)
self.setLayout(layout)
#fields not considered 'mandatory' as defaults are accepted
self.registerField("amountpower", results[0][1])
self.registerField("mincjamount", results[1][1])
self.registerField("maxrelfee", results[2][1])
self.registerField("maxabsfee", results[3][1])
self.registerField("mincjamount", results[0][1])
self.registerField("maxrelfee", results[1][1])
self.registerField("maxabsfee", results[2][1])

Loading…
Cancel
Save