diff --git a/jmclient/jmclient/schedule.py b/jmclient/jmclient/schedule.py index a598ecd..31b7543 100644 --- a/jmclient/jmclient/schedule.py +++ b/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 diff --git a/jmclient/test/test_schedule.py b/jmclient/test/test_schedule.py index 1c135e6..2b049f4 100644 --- a/jmclient/test/test_schedule.py +++ b/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 diff --git a/scripts/cli_options.py b/scripts/cli_options.py index d83b4bc..7fcdc63 100644 --- a/scripts/cli_options.py +++ b/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', diff --git a/scripts/qtsupport.py b/scripts/qtsupport.py index 36808d3..6e6d2b0 100644 --- a/scripts/qtsupport.py +++ b/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])