From e1bd0af1482f15444006b1f479a6512d7231ea42 Mon Sep 17 00:00:00 2001 From: Adlai Chandrasekhar Date: Sat, 5 Sep 2015 16:45:51 +0300 Subject: [PATCH 1/2] Bugfix: socks5 negotiation failure reporting fixes #220 --- lib/socks.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/socks.py b/lib/socks.py index 8792c9b5..bc3138a5 100644 --- a/lib/socks.py +++ b/lib/socks.py @@ -232,10 +232,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) From e12a70e58d7aa0c35c1a3e028cfe5170274f116d Mon Sep 17 00:00:00 2001 From: Adlai Chandrasekhar Date: Wed, 5 Aug 2015 19:35:51 +0300 Subject: [PATCH 2/2] combat dust thru utxo merge policy - adds an optional config section: [POLICY] - adds a merge_algorithm option in the new section - two sample merge algorithms are provided, beyond the default: + select_gradual still makes small transactions, clearing dust patiently + select_greedy collects dust as quickly as practical --- lib/common.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/lib/common.py b/lib/common.py index 66c3f0df..23fad877 100644 --- a/lib/common.py +++ b/lib/common.py @@ -5,7 +5,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 @@ -50,6 +50,10 @@ #port = 6697 #usessl = true #socks5 = true + +[POLICY] +#for dust sweeping, try merge_algorithm = gradual +merge_algorithm = default """ def load_program_config(): @@ -163,13 +167,74 @@ def debug_dump_object(obj, skip_fields=[]): else: 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 def get_utxos_by_mixdepth(self): @@ -187,7 +252,7 @@ def select_utxos(self, mixdepth, amount): utxo_list = self.get_utxos_by_mixdepth()[mixdepth] 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)) return dict([(i['utxo'], {'value': i['value'], 'address':