-
Notifications
You must be signed in to change notification settings - Fork 178
/
yg-privacyenhanced.py
executable file
·112 lines (98 loc) · 5.85 KB
/
yg-privacyenhanced.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python3
import random
import sys
from jmbase import get_log, jmprint, EXIT_ARGERROR
from jmbitcoin import amount_to_str
from jmclient import YieldGeneratorBasic, ygmain, jm_single
# This is a maker for the purposes of generating a yield from held bitcoins
# while maximising the difficulty of spying on blockchain activity.
# This is primarily attempted by randomizing all aspects of orders
# after transactions wherever possible.
# YIELD GENERATOR SETTINGS ARE NOW IN YOUR joinmarket.cfg CONFIG FILE
# (You can also use command line flags; see --help for this script).
jlog = get_log()
class YieldGeneratorPrivacyEnhanced(YieldGeneratorBasic):
def __init__(self, wallet_service, offerconfig):
super().__init__(wallet_service, offerconfig)
def select_input_mixdepth(self, available, offer, amount):
"""Mixdepths are in cyclic order and we select the mixdepth to
maximize the largest interval of non-available mixdepths by choosing
the first mixdepth available after the largest such interval.
This forces the biggest UTXOs to stay in a bulk of few mixdepths so
that the maker can always maximize the size of his orders even when
some coins are sent from the last to the first mixdepth"""
# We sort the available depths for linear scaling of the interval search
available = sorted(available.keys())
# For an available mixdepth, the smallest interval starting from this mixdepth
# containing all the other available mixdepths necessarily ends at the previous
# available mixdepth in the cyclic order. The successive difference of sorted
# depths is then the length of the largest interval ending at the same mixdepth
# without any available mixdepths, modulo the number of mixdepths if 0 is in it
# which is only the case for the first (in linear order) available mixdepth case
intervals = ([self.wallet_service.mixdepth + 1 + available[0] - available[-1]] + \
[(available[i+1] - available[i]) for i in range(len(available)-1)])
# We return the mixdepth value at which the largest interval without
# available mixdepths ends. Selecting this mixdepth will send the CoinJoin
# outputs closer to the others available mixdepths which are after in cyclical order
return available[max(range(len(available)), key = intervals.__getitem__)]
def create_my_orders(self):
mix_balance = self.get_available_mixdepths()
# We publish ONLY the maximum amount and use minsize for lower bound;
# leave it to oid_to_order to figure out the right depth to use.
f = '0'
if self.ordertype in ['swreloffer', 'sw0reloffer']:
f = self.cjfee_r
elif self.ordertype in ['swabsoffer', 'sw0absoffer']:
f = str(self.txfee_contribution + self.cjfee_a)
mix_balance = dict([(m, b) for m, b in mix_balance.items() if b > self.minsize])
if len(mix_balance) == 0:
jlog.error('You do not have the minimum required amount of coins'
' to be a maker: ' + str(self.minsize) + \
'\nTry setting txfee_contribution to zero and/or '
'lowering the minsize.')
return []
max_mix = max(mix_balance, key=mix_balance.get)
# randomizing the different values
randomize_txfee = int(random.uniform(
self.txfee_contribution * (1 - float(self.txfee_contribution_factor)),
self.txfee_contribution * (1 + float(self.txfee_contribution_factor))))
randomize_minsize = int(random.uniform(
self.minsize * (1 - float(self.size_factor)),
self.minsize * (1 + float(self.size_factor))))
if randomize_minsize < jm_single().DUST_THRESHOLD:
jlog.warn("Minsize was randomized to below dust; resetting to dust "
"threshold: " + amount_to_str(jm_single().DUST_THRESHOLD))
randomize_minsize = jm_single().DUST_THRESHOLD
possible_maxsize = mix_balance[max_mix] - max(jm_single().DUST_THRESHOLD, randomize_txfee)
randomize_maxsize = int(random.uniform(possible_maxsize * (1 - float(self.size_factor)),
possible_maxsize))
if self.ordertype in ['swabsoffer', 'sw0absoffer']:
randomize_cjfee = int(random.uniform(float(self.cjfee_a) * (1 - float(self.cjfee_factor)),
float(self.cjfee_a) * (1 + float(self.cjfee_factor))))
randomize_cjfee = randomize_cjfee + randomize_txfee
else:
randomize_cjfee = random.uniform(float(f) * (1 - float(self.cjfee_factor)),
float(f) * (1 + float(self.cjfee_factor)))
randomize_cjfee = "{0:.6f}".format(randomize_cjfee) # round to 6 decimals
order = {'oid': 0,
'ordertype': self.ordertype,
'minsize': randomize_minsize,
'maxsize': randomize_maxsize,
'txfee': randomize_txfee,
'cjfee': str(randomize_cjfee)}
# sanity check
assert order['minsize'] >= jm_single().DUST_THRESHOLD
assert order['minsize'] <= order['maxsize']
if order['ordertype'] in ['swreloffer', 'sw0reloffer']:
for i in range(20):
if order['txfee'] < (float(order['cjfee']) * order['minsize']):
break
order['txfee'] = int(order['txfee'] / 2)
jlog.info('Warning: too high txfee to be profitable, halving it to: ' + str(order['txfee']))
else:
jlog.error("Tx fee reduction algorithm failed. Quitting.")
sys.exit(EXIT_ARGERROR)
return [order]
if __name__ == "__main__":
ygmain(YieldGeneratorPrivacyEnhanced, nickserv_password='')
jmprint('done', "success")