Browse Source

make YGBasic part of jmclient package for reuse

master
Adam Gibson 9 years ago
parent
commit
13d065cd5d
No known key found for this signature in database
GPG Key ID: B3AE09F1E9A3197A
  1. 2
      jmclient/jmclient/__init__.py
  2. 118
      jmclient/jmclient/yieldgenerator.py
  3. 119
      scripts/yield-generator-basic.py

2
jmclient/jmclient/__init__.py

@ -39,7 +39,7 @@ from .taker_utils import (tumbler_taker_finished_update, restart_waiter,
tumbler_filter_orders_callback)
from .wallet_utils import wallet_tool_main
from .maker import Maker
from .yieldgenerator import YieldGenerator, ygmain
from .yieldgenerator import YieldGenerator, YieldGeneratorBasic, ygmain
# Set default logging handler to avoid "No handler found" warnings.
try:

118
jmclient/jmclient/yieldgenerator.py

@ -9,15 +9,17 @@ from optparse import OptionParser
from jmclient import (Maker, jm_single, get_network, load_program_config, get_log,
SegwitWallet, sync_wallet, JMClientProtocolFactory,
start_reactor)
start_reactor, calc_cj_fee)
jlog = get_log()
MAX_MIX_DEPTH = 5
# is a maker for the purposes of generating a yield from held
# bitcoins
class YieldGenerator(Maker):
"""A maker for the purposes of generating a yield from held
bitcoins, offering from the maximum mixdepth and trying to offer
the largest amount within the constraints of mixing depth isolation.
"""
__metaclass__ = abc.ABCMeta
statement_file = os.path.join('logs', 'yigen-statement.csv')
@ -65,6 +67,116 @@ class YieldGenerator(Maker):
a transaction into a block (e.g. announce orders)
"""
class YieldGeneratorBasic(YieldGenerator):
"""A simplest possible instantiation of a yieldgenerator.
It will often (but not always) reannounce orders after transactions,
thus is somewhat suboptimal in giving more information to spies.
"""
def __init__(self, wallet, offerconfig):
self.txfee, self.cjfee_a, self.cjfee_r, self.ordertype, self.minsize \
= offerconfig
super(YieldGeneratorBasic,self).__init__(wallet)
def create_my_orders(self):
mix_balance = self.wallet.get_balance_by_mixdepth()
if len([b for m, b in mix_balance.iteritems() if b > 0]) == 0:
jlog.error('do not have any coins left')
return []
# print mix_balance
max_mix = max(mix_balance, key=mix_balance.get)
f = '0'
if self.ordertype == 'swreloffer':
f = self.cjfee_r
#minimum size bumped if necessary such that you always profit
#least 50% of the miner fee
self.minsize = max(int(1.5 * self.txfee / float(self.cjfee_r)),
self.minsize)
elif self.ordertype == 'swabsoffer':
f = str(self.txfee + self.cjfee_a)
order = {'oid': 0,
'ordertype': self.ordertype,
'minsize': self.minsize,
'maxsize': mix_balance[max_mix] - max(
jm_single().DUST_THRESHOLD, self.txfee),
'txfee': self.txfee,
'cjfee': f}
# sanity check
assert order['minsize'] >= 0
assert order['maxsize'] > 0
if order['minsize'] > order['maxsize']:
jlog.info('minsize (' + str(order['minsize']) + ') > maxsize (' + str(
order['maxsize']) + ')')
return []
return [order]
def oid_to_order(self, offer, amount):
total_amount = amount + offer["txfee"]
mix_balance = self.wallet.get_balance_by_mixdepth()
max_mix = max(mix_balance, key=mix_balance.get)
filtered_mix_balance = [m
for m in mix_balance.iteritems()
if m[1] >= total_amount]
if not filtered_mix_balance:
return None, None, None
jlog.debug('mix depths that have enough = ' + str(filtered_mix_balance))
filtered_mix_balance = sorted(filtered_mix_balance, key=lambda x: x[0])
mixdepth = filtered_mix_balance[0][0]
jlog.info('filling offer, mixdepth=' + str(mixdepth))
# mixdepth is the chosen depth we'll be spending from
cj_addr = self.wallet.get_internal_addr((mixdepth + 1) %
self.wallet.max_mix_depth)
change_addr = self.wallet.get_internal_addr(mixdepth)
utxos = self.wallet.select_utxos(mixdepth, total_amount)
my_total_in = sum([va['value'] for va in utxos.values()])
real_cjfee = calc_cj_fee(offer["ordertype"], offer["cjfee"], amount)
change_value = my_total_in - amount - offer["txfee"] + real_cjfee
if change_value <= jm_single().DUST_THRESHOLD:
jlog.debug(('change value={} below dust threshold, '
'finding new utxos').format(change_value))
try:
utxos = self.wallet.select_utxos(
mixdepth, total_amount + jm_single().DUST_THRESHOLD)
except Exception:
jlog.info('dont have the required UTXOs to make a '
'output above the dust threshold, quitting')
return None, None, None
return utxos, cj_addr, change_addr
def on_tx_unconfirmed(self, offer, txid, removed_utxos):
self.tx_unconfirm_timestamp[offer["cjaddr"]] = int(time.time())
# if the balance of the highest-balance mixing depth change then
# reannounce it
oldoffer = self.offerlist[0] if len(self.offerlist) > 0 else None
newoffers = self.create_my_orders()
if len(newoffers) == 0:
return [0], [] # cancel old order
if oldoffer:
if oldoffer['maxsize'] == newoffers[0]['maxsize']:
return [], [] # change nothing
# announce new order, replacing the old order
return [], [newoffers[0]]
def on_tx_confirmed(self, offer, confirmations, txid):
if offer["cjaddr"] in self.tx_unconfirm_timestamp:
confirm_time = int(time.time()) - self.tx_unconfirm_timestamp[
offer["cjaddr"]]
else:
confirm_time = 0
timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
real_cjfee = calc_cj_fee(offer["offer"]["ordertype"],
offer["offer"]["cjfee"], offer["amount"])
self.log_statement([timestamp, offer["amount"], len(
offer["utxos"]), sum([av['value'] for av in offer["utxos"].values(
)]), real_cjfee, real_cjfee - offer["offer"]["txfee"], round(
confirm_time / 60.0, 2), ''])
return self.on_tx_unconfirmed(offer, txid, None)
def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='swreloffer',
nickserv_password='', minsize=100000, gaplimit=6):

119
scripts/yield-generator-basic.py

@ -5,7 +5,11 @@ import datetime
import os
import time
from jmclient import YieldGenerator, ygmain, get_log, jm_single, calc_cj_fee
from jmclient import (YieldGenerator, YieldGeneratorBasic, ygmain, get_log,
jm_single, calc_cj_fee)
"""THESE SETTINGS CAN SIMPLY BE EDITED BY HAND IN THIS FILE:
"""
txfee = 1000
cjfee_a = 200
cjfee_r = '0.0002'
@ -16,119 +20,6 @@ gaplimit = 6
jlog = get_log()
# is a maker for the purposes of generating a yield from held
# bitcoins, offering from the maximum mixdepth and trying to offer
# the largest amount within the constraints of mixing depth isolation.
# It will often (but not always) reannounce orders after transactions,
# thus is somewhat suboptimal in giving more information to spies.
class YieldGeneratorBasic(YieldGenerator):
def __init__(self, wallet, offerconfig):
self.txfee, self.cjfee_a, self.cjfee_r, self.ordertype, self.minsize \
= offerconfig
super(YieldGeneratorBasic,self).__init__(wallet)
def create_my_orders(self):
mix_balance = self.wallet.get_balance_by_mixdepth()
if len([b for m, b in mix_balance.iteritems() if b > 0]) == 0:
jlog.error('do not have any coins left')
return []
# print mix_balance
max_mix = max(mix_balance, key=mix_balance.get)
f = '0'
if self.ordertype == 'swreloffer':
f = self.cjfee_r
#minimum size bumped if necessary such that you always profit
#least 50% of the miner fee
self.minsize = max(int(1.5 * self.txfee / float(self.cjfee_r)),
max_minsize)
elif self.ordertype == 'swabsoffer':
f = str(self.txfee + self.cjfee_a)
order = {'oid': 0,
'ordertype': self.ordertype,
'minsize': self.minsize,
'maxsize': mix_balance[max_mix] - max(
jm_single().DUST_THRESHOLD,txfee),
'txfee': self.txfee,
'cjfee': f}
# sanity check
assert order['minsize'] >= 0
assert order['maxsize'] > 0
if order['minsize'] > order['maxsize']:
jlog.info('minsize (' + str(order['minsize']) + ') > maxsize (' + str(
order['maxsize']) + ')')
return []
return [order]
def oid_to_order(self, offer, amount):
total_amount = amount + offer["txfee"]
mix_balance = self.wallet.get_balance_by_mixdepth()
max_mix = max(mix_balance, key=mix_balance.get)
filtered_mix_balance = [m
for m in mix_balance.iteritems()
if m[1] >= total_amount]
if not filtered_mix_balance:
return None, None, None
jlog.debug('mix depths that have enough = ' + str(filtered_mix_balance))
filtered_mix_balance = sorted(filtered_mix_balance, key=lambda x: x[0])
mixdepth = filtered_mix_balance[0][0]
jlog.info('filling offer, mixdepth=' + str(mixdepth))
# mixdepth is the chosen depth we'll be spending from
cj_addr = self.wallet.get_internal_addr((mixdepth + 1) %
self.wallet.max_mix_depth)
change_addr = self.wallet.get_internal_addr(mixdepth)
utxos = self.wallet.select_utxos(mixdepth, total_amount)
my_total_in = sum([va['value'] for va in utxos.values()])
real_cjfee = calc_cj_fee(offer["ordertype"], offer["cjfee"], amount)
change_value = my_total_in - amount - offer["txfee"] + real_cjfee
if change_value <= jm_single().DUST_THRESHOLD:
jlog.debug(('change value={} below dust threshold, '
'finding new utxos').format(change_value))
try:
utxos = self.wallet.select_utxos(
mixdepth, total_amount + jm_single().DUST_THRESHOLD)
except Exception:
jlog.info('dont have the required UTXOs to make a '
'output above the dust threshold, quitting')
return None, None, None
return utxos, cj_addr, change_addr
def on_tx_unconfirmed(self, offer, txid, removed_utxos):
self.tx_unconfirm_timestamp[offer["cjaddr"]] = int(time.time())
# if the balance of the highest-balance mixing depth change then
# reannounce it
oldoffer = self.offerlist[0] if len(self.offerlist) > 0 else None
newoffers = self.create_my_orders()
if len(newoffers) == 0:
return [0], [] # cancel old order
if oldoffer:
if oldoffer['maxsize'] == newoffers[0]['maxsize']:
return [], [] # change nothing
# announce new order, replacing the old order
return [], [newoffers[0]]
def on_tx_confirmed(self, offer, confirmations, txid):
if offer["cjaddr"] in self.tx_unconfirm_timestamp:
confirm_time = int(time.time()) - self.tx_unconfirm_timestamp[
offer["cjaddr"]]
else:
confirm_time = 0
timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
real_cjfee = calc_cj_fee(offer["offer"]["ordertype"],
offer["offer"]["cjfee"], offer["amount"])
self.log_statement([timestamp, offer["amount"], len(
offer["utxos"]), sum([av['value'] for av in offer["utxos"].values(
)]), real_cjfee, real_cjfee - offer["offer"]["txfee"], round(
confirm_time / 60.0, 2), ''])
return self.on_tx_unconfirmed(offer, txid, None)
if __name__ == "__main__":
ygmain(YieldGeneratorBasic, txfee=txfee, cjfee_a=cjfee_a,
cjfee_r=cjfee_r, ordertype=ordertype,

Loading…
Cancel
Save