You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

132 lines
5.2 KiB

#! /usr/bin/env python
from __future__ import absolute_import, print_function
import base64
import pprint
import random
import sqlite3
import sys
import time
import threading
import json
from decimal import InvalidOperation, Decimal
from daemon.protocol import JM_VERSION
from base.support import get_log, joinmarket_alert, DUST_THRESHOLD
from daemon.irc import B_PER_SEC
log = get_log()
class JMTakerError(Exception):
pass
class OrderbookWatch(object):
def set_msgchan(self, msgchan):
self.msgchan = msgchan
self.msgchan.register_orderbookwatch_callbacks(self.on_order_seen,
self.on_order_cancel)
self.msgchan.register_channel_callbacks(
self.on_welcome, self.on_set_topic, None, self.on_disconnect,
self.on_nick_leave, None)
self.dblock = threading.Lock()
con = sqlite3.connect(":memory:", check_same_thread=False)
con.row_factory = sqlite3.Row
self.db = con.cursor()
self.db.execute("CREATE TABLE orderbook(counterparty TEXT, "
"oid INTEGER, ordertype TEXT, minsize INTEGER, "
"maxsize INTEGER, txfee INTEGER, cjfee TEXT);")
@staticmethod
def on_set_topic(newtopic):
chunks = newtopic.split('|')
for msg in chunks[1:]:
try:
msg = msg.strip()
params = msg.split(' ')
min_version = int(params[0])
max_version = int(params[1])
alert = msg[msg.index(params[1]) + len(params[1]):].strip()
except ValueError, IndexError:
continue
if min_version < JM_VERSION < max_version:
print('=' * 60)
print('JOINMARKET ALERT')
print(alert)
print('=' * 60)
joinmarket_alert[0] = alert
def on_order_seen(self, counterparty, oid, ordertype, minsize, maxsize,
txfee, cjfee):
try:
self.dblock.acquire(True)
if int(oid) < 0 or int(oid) > sys.maxint:
log.debug("Got invalid order ID: " + oid + " from " +
counterparty)
return (False, [])
# delete orders eagerly, so in case a buggy maker sends an
# invalid offer, we won't accidentally !fill based on the ghost
# of its previous message.
self.db.execute(
("DELETE FROM orderbook WHERE counterparty=? "
"AND oid=?;"), (counterparty, oid))
# now validate the remaining fields
if int(minsize) < 0 or int(minsize) > 21 * 10**14:
log.debug("Got invalid minsize: {} from {}".format(
minsize, counterparty))
return (False, [])
if int(minsize) < DUST_THRESHOLD:
minsize = DUST_THRESHOLD
log.debug("{} has dusty minsize, capping at {}".format(
counterparty, minsize))
# do not pass return, go not drop this otherwise fine offer
if int(maxsize) < 0 or int(maxsize) > 21 * 10**14:
log.debug("Got invalid maxsize: " + maxsize + " from " +
counterparty)
return (False, [])
if int(txfee) < 0:
log.debug("Got invalid txfee: {} from {}".format(txfee,
counterparty))
return (False, [])
if int(minsize) > int(maxsize):
fmt = ("Got minsize bigger than maxsize: {} - {} "
"from {}").format
log.debug(fmt(minsize, maxsize, counterparty))
return (False, [])
if ordertype == 'absoffer' and not isinstance(cjfee, int):
try:
cjfee = int(cjfee)
except ValueError:
log.debug("Got non integer coinjoin fee: " + str(cjfee) +
" for an absoffer from " + counterparty)
return (False, [])
self.db.execute(
'INSERT INTO orderbook VALUES(?, ?, ?, ?, ?, ?, ?);',
(counterparty, oid, ordertype, minsize, maxsize, txfee,
str(Decimal(cjfee)))) # any parseable Decimal is a valid cjfee
except InvalidOperation:
log.debug("Got invalid cjfee: " + cjfee + " from " + counterparty)
except Exception as e:
log.debug("Error parsing order " + oid + " from " + counterparty)
log.debug("Exception was: " + repr(e))
finally:
self.dblock.release()
return (True, [])
def on_order_cancel(self, counterparty, oid):
with self.dblock:
self.db.execute(
("DELETE FROM orderbook WHERE "
"counterparty=? AND oid=?;"), (counterparty, oid))
def on_nick_leave(self, nick):
with self.dblock:
self.db.execute('DELETE FROM orderbook WHERE counterparty=?;',
(nick,))
def on_disconnect(self):
with self.dblock:
self.db.execute('DELETE FROM orderbook;')