Skip to content

Commit

Permalink
Merge pull request JoinMarket-Org#173 from adlai/mergy-police
Browse files Browse the repository at this point in the history
combat dust thru utxo merge policies
[gitreformat yapf-ify (github/ghtdak) on Fri Dec  4 04:51:20 2015]
[from commit: 6f3a98c]
  • Loading branch information
chris-belcher committed Sep 5, 2015
2 parents 0c9df5a + 9bfbfbb commit b06b6ae
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 7 deletions.
72 changes: 69 additions & 3 deletions lib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys, datetime, json, time, pprint, threading, getpass
import random
import blockchaininterface, slowaes
from ConfigParser import SafeConfigParser
from ConfigParser import SafeConfigParser, NoSectionError
import os, io, itertools

JM_VERSION = 2
Expand Down Expand Up @@ -49,6 +49,10 @@
#port = 6697
#usessl = true
#socks5 = true
[POLICY]
#for dust sweeping, try merge_algorithm = gradual
merge_algorithm = default
"""


Expand Down Expand Up @@ -179,14 +183,76 @@ def debug_dump_object(obj, skip_fields=[]):
debug(str(v))


def select_gradual(unspent, value):
'''
UTXO selection algorithm for gradual dust reduction
If possible, combines outputs, picking as few as possible of the largest
utxos less than the target value; if the target value is larger than the
sum of all smaller utxos, uses the smallest utxo larger than the value.
'''
value, key = int(value), lambda u: u["value"]
high = sorted([u for u in unspent if key(u) >= value], key=key)
low = sorted([u for u in unspent if key(u) < value], key=key)
lowsum = reduce(lambda x, y: x + y, map(key, low), 0)
if value > lowsum:
if len(high) == 0:
raise Exception('Not enough funds')
else:
return [high[0]]
else:
start, end, total = 0, 0, 0
while total < value:
total += low[end]['value']
end += 1
while total >= value + low[start]['value']:
total -= low[start]['value']
start += 1
return low[start:end]


def select_greedy(unspent, value):
'''
UTXO selection algorithm for rapid dust reduction
Combines the shortest run of utxos (sorted by size, from smallest) which
exceeds the target value; if the target value is larger than the sum of
all smaller utxos, uses the smallest utxo larger than the target value.
'''
value, key = int(value), lambda u: u["value"]
high = sorted([u for u in unspent if key(u) >= value], key=key)
low = sorted([u for u in unspent if key(u) < value], key=key)
lowsum = reduce(lambda x, y: x + y, map(key, low), 0)
print((high, low, lowsum))
if value > lowsum:
if len(high) == 0:
raise Exception('Not enough funds')
else:
return [high[0]]
else:
end, total = 0, 0
while total < value:
total += low[end]['value']
end += 1
return low[0:end]


class AbstractWallet(object):
'''
Abstract wallet for use with JoinMarket
Mostly written with Wallet in mind, the default JoinMarket HD wallet
'''

def __init__(self):
pass
self.utxo_selector = btc.select # default fallback: upstream
try:
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") != "default":
raise Exception("Unknown merge algorithm")
except NoSectionError:
debug("Please add the new [POLICY] section to your config")
debug("Set therein thine merge_algorithm as default or gradual")

def get_key_from_addr(self, addr):
return None
Expand All @@ -211,7 +277,7 @@ def select_utxos(self, mixdepth, amount):
unspent = [{'utxo': utxo,
'value': addrval['value']}
for utxo, addrval in utxo_list.iteritems()]
inputs = btc.select(unspent, amount)
inputs = self.utxo_selector(unspent, amount)
debug('for mixdepth=' + str(mixdepth) + ' amount=' + str(amount) +
' selected:')
debug(pprint.pformat(inputs))
Expand Down
5 changes: 1 addition & 4 deletions lib/socks.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,7 @@ def __negotiatesocks5(self, destaddr, destport):
elif resp[1] != "\x00":
# Connection failed
self.close()
if ord(resp[1]) <= 8:
raise Socks5Error(ord(resp[1]), _generalerrors[ord(resp[1])])
else:
raise Socks5Error(9, _generalerrors[9])
raise Socks5Error(_socks5errors[min(9, ord(resp[1]))])
# Get the bound address/port
elif resp[3] == "\x01":
boundaddr = self.__recvall(4)
Expand Down

0 comments on commit b06b6ae

Please sign in to comment.