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

Commit

Permalink
configurable thresholds for utxo merge policies
Browse files Browse the repository at this point in the history
  • Loading branch information
adlai committed Feb 4, 2016
1 parent 816242d commit ce9346e
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 18 deletions.
9 changes: 4 additions & 5 deletions joinmarket/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,10 @@ def jm_single():
maker_timeout_sec = 30
[POLICY]
# for dust sweeping, try merge_algorithm = gradual
# for more rapid dust sweeping, try merge_algorithm = greedy
# for most rapid dust sweeping, try merge_algorithm = greediest
# but don't forget to bump your miner fees!
merge_algorithm = default
# this knob is a list of ints, being each the amount of utxos in a mixdepth
# sufficient for kicking in the next-mergiest utxo selection algorithm. the
# default gives sane behavior; it is not clear what change improves privacy.
merge_algorithm = [7, 14, 28]
# the fee estimate is based on a projection of how many satoshis
# per kB are needed to get in one of the next N blocks, N set here
# as the value of 'tx_fees'. This estimate is high if you set N=1,
Expand Down
16 changes: 16 additions & 0 deletions joinmarket/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

from math import exp

import bitcoin # we only need bitcoin.select()

# todo: this was the date format used in the original debug(). Use it?
# logging.basicConfig(filename='logs/joinmarket.log',
# stream=sys.stdout,
Expand Down Expand Up @@ -177,6 +179,20 @@ def select_greediest(unspent, value):
end += 1
return low[0:end]

# ordered from most dusty (arguably, most private) to most mergiest (cheaper!)
selectors = [bitcoin.select, select_gradual, select_greedy, select_greediest]

def utxo_selector(configured_levels):
def select(unspent, value):
length = len(unspent) # NB - counted only within each mixdepth
try:
for i in xrange(len(configured_levels)):
if length < configured_levels[i]:
return selectors[i]
except IndexError: # luser configured more merge levels than algos
log.debug("I'm sorry, Dave, but I can't let you merge that!")
return selectors[-1] # just use the greediest one available
return select

def calc_cj_fee(ordertype, cjfee, cj_amount):
if ordertype == 'absorder':
Expand Down
34 changes: 21 additions & 13 deletions joinmarket/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import pprint
import sys
import ast
from decimal import Decimal

from ConfigParser import NoSectionError
Expand All @@ -13,8 +14,7 @@
from joinmarket.blockchaininterface import BitcoinCoreInterface
from joinmarket.configure import jm_single, get_network, get_p2pk_vbyte

from joinmarket.support import get_log, select_gradual, select_greedy, \
select_greediest
from joinmarket.support import get_log, utxo_selector

log = get_log()

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

def __init__(self):
self.max_mix_depth = 0
self.utxo_selector = btc.select # default fallback: upstream
try:
config = jm_single().config
if config.get("POLICY", "merge_algorithm") == "gradual":
self.utxo_selector = select_gradual
elif config.get("POLICY", "merge_algorithm") == "greedy":
self.utxo_selector = select_greedy
elif config.get("POLICY", "merge_algorithm") == "greediest":
self.utxo_selector = select_greediest
elif config.get("POLICY", "merge_algorithm") != "default":
raise Exception("Unknown merge algorithm")
policy = jm_single().config.get("POLICY", "merge_algorithm")
except NoSectionError:
pass
policy = "default" # maintain backwards compatibility!
if policy == "default":
self.merge_policy = [42] # well, almost (python lacks infinites)
elif policy == "gradual":
self.merge_policy = [6] # never goes beyond gradual
elif policy == "greedy":
self.merge_policy = [7, 7] # skip gradual, go greedy
elif policy == "greediest":
self.merge_policy = [8, 8, 8] # straight to greediest
else:
try: # stop supporting word configs, someday...
self.merge_policy = ast.literal_eval(policy)
if ((type(self.merge_policy) is not list) or
any(type(level) is not int for level in self.merge_policy)):
raise Exception("Merge policy must be a list of ints")
except ValueError:
raise Exception("Unparseable merge policy: "+policy)
self.utxo_selector = utxo_selector(self.merge_policy)

def get_key_from_addr(self, addr):
return None
Expand Down

0 comments on commit ce9346e

Please sign in to comment.