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

Commit 24dc385

Browse files
committed
configurable thresholds for utxo merge policies
still no tests
1 parent 6272583 commit 24dc385

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
@@ -121,16 +121,15 @@ def jm_single():
121121
confirm_timeout_hours = 6
122122
123123
[POLICY]
124-
# for dust sweeping, try merge_algorithm = gradual
125-
# for more rapid dust sweeping, try merge_algorithm = greedy
126-
# for most rapid dust sweeping, try merge_algorithm = greediest
127-
# but don't forget to bump your miner fees!
128-
merge_algorithm = default
129124
# For takers: the minimum number of makers you allow in a transaction
130125
# to complete, accounting for the fact that some makers might not be
131126
# responsive. Should be an integer >=2 for privacy, or set to 0 if you
132127
# want to disallow any reduction from your chosen number of makers.
133128
minimum_makers = 2
129+
# this knob is a list of ints, being each the amount of utxos in a mixdepth
130+
# sufficient for kicking in the next-mergiest utxo selection algorithm. the
131+
# default errs on the side of privacy; lower values make tracking easier.
132+
merge_algorithm = [42, 59, 72]
134133
# the fee estimate is based on a projection of how many satoshis
135134
# per kB are needed to get in one of the next N blocks, N set here
136135
# as the value of 'tx_fees'. This estimate is high if you set N=2,

joinmarket/support.py

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

1111
from math import exp
1212

13+
import bitcoin # we only need bitcoin.select()
14+
1315
# todo: this was the date format used in the original debug(). Use it?
1416
# logging.basicConfig(filename='logs/joinmarket.log',
1517
# stream=sys.stdout,
@@ -174,6 +176,20 @@ def select_greediest(unspent, value):
174176
end += 1
175177
return low[0:end]
176178

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

178194
def calc_cj_fee(ordertype, cjfee, cj_amount):
179195
if ordertype == 'absoffer':

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, RegtestBitcoinCoreInterface
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

@@ -46,19 +46,27 @@ def __init__(self):
4646
#some consumer scripts don't use an unspent, this marks it
4747
#as specifically absent (rather than just empty).
4848
self.unspent = None
49-
self.utxo_selector = btc.select # default fallback: upstream
5049
try:
51-
config = jm_single().config
52-
if config.get("POLICY", "merge_algorithm") == "gradual":
53-
self.utxo_selector = select_gradual
54-
elif config.get("POLICY", "merge_algorithm") == "greedy":
55-
self.utxo_selector = select_greedy
56-
elif config.get("POLICY", "merge_algorithm") == "greediest":
57-
self.utxo_selector = select_greediest
58-
elif config.get("POLICY", "merge_algorithm") != "default":
59-
raise Exception("Unknown merge algorithm")
50+
policy = jm_single().config.get("POLICY", "merge_algorithm")
6051
except NoSectionError:
61-
pass
52+
policy = "default" # maintain backwards compatibility!
53+
if policy == "default":
54+
self.merge_policy = [42] # well, almost (python lacks infinites)
55+
elif policy == "gradual":
56+
self.merge_policy = [60] # never goes beyond gradual
57+
elif policy == "greedy":
58+
self.merge_policy = [70, 70] # skip gradual, go greedy
59+
elif policy == "greediest":
60+
self.merge_policy = [80, 80, 80] # straight to greediest
61+
else:
62+
try: # stop supporting word configs, someday...
63+
self.merge_policy = ast.literal_eval(policy)
64+
if ((type(self.merge_policy) is not list) or
65+
any(type(level) is not int for level in self.merge_policy)):
66+
raise Exception("Merge policy must be a list of ints")
67+
except ValueError:
68+
raise Exception("Unparseable merge policy: "+policy)
69+
self.utxo_selector = utxo_selector(self.merge_policy)
6270

6371
def get_key_from_addr(self, addr):
6472
return None

0 commit comments

Comments
 (0)