Skip to content
This repository was archived by the owner on May 13, 2022. It is now read-only.

Commit ac009e7

Browse files
committed
configurable thresholds for utxo merge policies
1 parent 816242d commit ac009e7

File tree

3 files changed

+41
-18
lines changed

3 files changed

+41
-18
lines changed

joinmarket/configure.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,10 @@ def jm_single():
123123
maker_timeout_sec = 30
124124
125125
[POLICY]
126-
# for dust sweeping, try merge_algorithm = gradual
127-
# for more rapid dust sweeping, try merge_algorithm = greedy
128-
# for most rapid dust sweeping, try merge_algorithm = greediest
129-
# but don't forget to bump your miner fees!
130-
merge_algorithm = default
126+
# this knob is a list of ints, being each the amount of utxos in a mixdepth
127+
# sufficient for kicking in the next-mergiest utxo selection algorithm. the
128+
# default gives sane behavior; it is not clear what change improves privacy.
129+
merge_algorithm = [7, 14, 28]
131130
# the fee estimate is based on a projection of how many satoshis
132131
# per kB are needed to get in one of the next N blocks, N set here
133132
# as the value of 'tx_fees'. This estimate is high if you set N=1,

joinmarket/support.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
from math import exp
1919

20+
import bitcoin # we only need bitcoin.select()
21+
2022
# todo: this was the date format used in the original debug(). Use it?
2123
# logging.basicConfig(filename='logs/joinmarket.log',
2224
# stream=sys.stdout,
@@ -177,6 +179,20 @@ def select_greediest(unspent, value):
177179
end += 1
178180
return low[0:end]
179181

182+
# ordered from most dusty (arguably, most private) to most mergiest (cheaper!)
183+
selectors = [bitcoin.select, select_gradual, select_greedy, select_greediest]
184+
185+
def utxo_selector(configured_levels):
186+
def select(unspent, value):
187+
length = len(unspent) # NB - counted only within each mixdepth
188+
try:
189+
for i in xrange(len(configured_levels)):
190+
if length < configured_levels[i]:
191+
return selectors[i](unspent, value)
192+
except IndexError: # luser configured more levels than algos
193+
log.debug("I'm sorry, Dave, but I can't let you merge that!")
194+
return selectors[-1](unspent, value) # just use the greediest one
195+
return select
180196

181197
def calc_cj_fee(ordertype, cjfee, cj_amount):
182198
if ordertype == 'absorder':

joinmarket/wallet.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import pprint
55
import sys
6+
import ast
67
from decimal import Decimal
78

89
from ConfigParser import NoSectionError
@@ -13,8 +14,7 @@
1314
from joinmarket.blockchaininterface import BitcoinCoreInterface
1415
from joinmarket.configure import jm_single, get_network, get_p2pk_vbyte
1516

16-
from joinmarket.support import get_log, select_gradual, select_greedy, \
17-
select_greediest
17+
from joinmarket.support import get_log, utxo_selector
1818

1919
log = get_log()
2020

@@ -38,19 +38,27 @@ class AbstractWallet(object):
3838

3939
def __init__(self):
4040
self.max_mix_depth = 0
41-
self.utxo_selector = btc.select # default fallback: upstream
4241
try:
43-
config = jm_single().config
44-
if config.get("POLICY", "merge_algorithm") == "gradual":
45-
self.utxo_selector = select_gradual
46-
elif config.get("POLICY", "merge_algorithm") == "greedy":
47-
self.utxo_selector = select_greedy
48-
elif config.get("POLICY", "merge_algorithm") == "greediest":
49-
self.utxo_selector = select_greediest
50-
elif config.get("POLICY", "merge_algorithm") != "default":
51-
raise Exception("Unknown merge algorithm")
42+
policy = jm_single().config.get("POLICY", "merge_algorithm")
5243
except NoSectionError:
53-
pass
44+
policy = "default" # maintain backwards compatibility!
45+
if policy == "default":
46+
self.merge_policy = [42] # well, almost (python lacks infinites)
47+
elif policy == "gradual":
48+
self.merge_policy = [6] # never goes beyond gradual
49+
elif policy == "greedy":
50+
self.merge_policy = [7, 7] # skip gradual, go greedy
51+
elif policy == "greediest":
52+
self.merge_policy = [8, 8, 8] # straight to greediest
53+
else:
54+
try: # stop supporting word configs, someday...
55+
self.merge_policy = ast.literal_eval(policy)
56+
if ((type(self.merge_policy) is not list) or
57+
any(type(level) is not int for level in self.merge_policy)):
58+
raise Exception("Merge policy must be a list of ints")
59+
except ValueError:
60+
raise Exception("Unparseable merge policy: "+policy)
61+
self.utxo_selector = utxo_selector(self.merge_policy)
5462

5563
def get_key_from_addr(self, addr):
5664
return None

0 commit comments

Comments
 (0)