mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-22 19:49:20 +00:00
Let SQLite handle all query parameters.
Some checks failed
lint / build (3.12) (push) Has been cancelled
Some checks failed
lint / build (3.12) (push) Has been cancelled
This commit is contained in:
parent
fa8764342e
commit
bd571702cb
5 changed files with 223 additions and 154 deletions
|
@ -93,6 +93,7 @@ from .db import (
|
||||||
CURRENT_DB_VERSION,
|
CURRENT_DB_VERSION,
|
||||||
EventLog,
|
EventLog,
|
||||||
firstOrNone,
|
firstOrNone,
|
||||||
|
getOrderByStr,
|
||||||
KnownIdentity,
|
KnownIdentity,
|
||||||
MessageLink,
|
MessageLink,
|
||||||
Notification,
|
Notification,
|
||||||
|
@ -936,13 +937,10 @@ class BasicSwap(BaseApp):
|
||||||
def start(self):
|
def start(self):
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(f"Starting BasicSwap {__version__}, database v{self.db_version}\n\n")
|
||||||
"Starting BasicSwap %s, database v%d\n\n", __version__, self.db_version
|
|
||||||
)
|
|
||||||
self.log.info(f"Python version: {platform.python_version()}")
|
self.log.info(f"Python version: {platform.python_version()}")
|
||||||
self.log.info(f"SQLite version: {sqlite3.sqlite_version}")
|
self.log.info(f"SQLite version: {sqlite3.sqlite_version}")
|
||||||
|
self.log.info(f"Timezone offset: {time.timezone} ({time.tzname[0]})")
|
||||||
self.log.info("Timezone offset: %d (%s)", time.timezone, time.tzname[0])
|
|
||||||
|
|
||||||
upgradeDatabase(self, self.db_version)
|
upgradeDatabase(self, self.db_version)
|
||||||
upgradeDatabaseData(self, self.db_data_version)
|
upgradeDatabaseData(self, self.db_data_version)
|
||||||
|
@ -1322,8 +1320,8 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
coin_id = int(coin_type)
|
coin_id = int(coin_type)
|
||||||
info_type = 1 # wallet
|
info_type = 1 # wallet
|
||||||
query_str = f"DELETE FROM wallets WHERE coin_id = {coin_id} AND balance_type = {info_type}"
|
query_str = "DELETE FROM wallets WHERE coin_id = ? AND balance_type = ?"
|
||||||
cursor.execute(query_str)
|
cursor.execute(query_str, (coin_id, info_type))
|
||||||
finally:
|
finally:
|
||||||
self.closeDB(cursor)
|
self.closeDB(cursor)
|
||||||
|
|
||||||
|
@ -1693,7 +1691,8 @@ class BasicSwap(BaseApp):
|
||||||
)
|
)
|
||||||
|
|
||||||
use_cursor.execute(
|
use_cursor.execute(
|
||||||
f"DELETE FROM notifications WHERE record_id NOT IN (SELECT record_id FROM notifications WHERE active_ind=1 ORDER BY created_at ASC LIMIT {self._keep_notifications})"
|
"DELETE FROM notifications WHERE record_id NOT IN (SELECT record_id FROM notifications WHERE active_ind=1 ORDER BY created_at ASC LIMIT ?)",
|
||||||
|
(self._keep_notifications,),
|
||||||
)
|
)
|
||||||
|
|
||||||
if show_event:
|
if show_event:
|
||||||
|
@ -1709,7 +1708,8 @@ class BasicSwap(BaseApp):
|
||||||
def buildNotificationsCache(self, cursor):
|
def buildNotificationsCache(self, cursor):
|
||||||
self._notifications_cache.clear()
|
self._notifications_cache.clear()
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
f"SELECT created_at, event_type, event_data FROM notifications WHERE active_ind = 1 ORDER BY created_at ASC LIMIT {self._show_notifications}"
|
"SELECT created_at, event_type, event_data FROM notifications WHERE active_ind = 1 ORDER BY created_at ASC LIMIT ?",
|
||||||
|
(self._keep_notifications,),
|
||||||
)
|
)
|
||||||
for entry in q:
|
for entry in q:
|
||||||
self._notifications_cache[entry[0]] = (
|
self._notifications_cache[entry[0]] = (
|
||||||
|
@ -1810,30 +1810,32 @@ class BasicSwap(BaseApp):
|
||||||
try:
|
try:
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
|
|
||||||
query_str = (
|
query_str: str = (
|
||||||
"SELECT address, label, num_sent_bids_successful, num_recv_bids_successful, "
|
"SELECT address, label, num_sent_bids_successful, num_recv_bids_successful, "
|
||||||
+ " num_sent_bids_rejected, num_recv_bids_rejected, num_sent_bids_failed, num_recv_bids_failed, "
|
+ " num_sent_bids_rejected, num_recv_bids_rejected, num_sent_bids_failed, num_recv_bids_failed, "
|
||||||
+ " automation_override, visibility_override, note "
|
+ " automation_override, visibility_override, note "
|
||||||
+ " FROM knownidentities "
|
+ " FROM knownidentities "
|
||||||
+ " WHERE active_ind = 1 "
|
+ " WHERE active_ind = 1 "
|
||||||
)
|
)
|
||||||
|
query_data: dict = {}
|
||||||
|
|
||||||
address = filters.get("address", None)
|
address = filters.get("address", None)
|
||||||
if address is not None:
|
if address is not None:
|
||||||
query_str += f' AND address = "{address}" '
|
query_str += " AND address = :address "
|
||||||
|
query_data["address"] = address
|
||||||
|
|
||||||
sort_dir = filters.get("sort_dir", "DESC").upper()
|
query_str += getOrderByStr(filters)
|
||||||
sort_by = filters.get("sort_by", "created_at")
|
|
||||||
query_str += f" ORDER BY {sort_by} {sort_dir}"
|
|
||||||
|
|
||||||
limit = filters.get("limit", None)
|
limit = filters.get("limit", None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
query_str += f" LIMIT {limit}"
|
query_str += " LIMIT :limit"
|
||||||
|
query_data["limit"] = limit
|
||||||
offset = filters.get("offset", None)
|
offset = filters.get("offset", None)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
query_str += f" OFFSET {offset}"
|
query_str += " OFFSET :offset"
|
||||||
|
query_data["offset"] = offset
|
||||||
|
|
||||||
q = cursor.execute(query_str)
|
q = cursor.execute(query_str, query_data)
|
||||||
rv = []
|
rv = []
|
||||||
for row in q:
|
for row in q:
|
||||||
identity = {
|
identity = {
|
||||||
|
@ -2821,9 +2823,12 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
def countBidEvents(self, bid, event_type, cursor):
|
def countBidEvents(self, bid, event_type, cursor):
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
"SELECT COUNT(*) FROM eventlog WHERE linked_type = {} AND linked_id = x'{}' AND event_type = {}".format(
|
"SELECT COUNT(*) FROM eventlog WHERE linked_type = :linked_type AND linked_id = :linked_id AND event_type = :event_type",
|
||||||
int(Concepts.BID), bid.bid_id.hex(), int(event_type)
|
{
|
||||||
)
|
"linked_type": int(Concepts.BID),
|
||||||
|
"linked_id": bid.bid_id,
|
||||||
|
"event_type": int(event_type),
|
||||||
|
},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
return q[0]
|
return q[0]
|
||||||
|
|
||||||
|
@ -3212,20 +3217,20 @@ class BasicSwap(BaseApp):
|
||||||
def list_bid_events(self, bid_id: bytes, cursor):
|
def list_bid_events(self, bid_id: bytes, cursor):
|
||||||
query_str = (
|
query_str = (
|
||||||
"SELECT created_at, event_type, event_msg FROM eventlog "
|
"SELECT created_at, event_type, event_msg FROM eventlog "
|
||||||
+ "WHERE active_ind = 1 AND linked_type = {} AND linked_id = x'{}' ".format(
|
+ "WHERE active_ind = 1 AND linked_type = :linked_type AND linked_id = :linked_id "
|
||||||
Concepts.BID, bid_id.hex()
|
|
||||||
)
|
)
|
||||||
|
q = cursor.execute(
|
||||||
|
query_str, {"linked_type": int(Concepts.BID), "linked_id": bid_id}
|
||||||
)
|
)
|
||||||
q = cursor.execute(query_str)
|
|
||||||
events = []
|
events = []
|
||||||
for row in q:
|
for row in q:
|
||||||
events.append({"at": row[0], "desc": describeEventEntry(row[1], row[2])})
|
events.append({"at": row[0], "desc": describeEventEntry(row[1], row[2])})
|
||||||
|
|
||||||
query_str = (
|
query_str = (
|
||||||
"SELECT created_at, trigger_at FROM actions "
|
"SELECT created_at, trigger_at FROM actions "
|
||||||
+ "WHERE active_ind = 1 AND linked_id = x'{}' ".format(bid_id.hex())
|
+ "WHERE active_ind = 1 AND linked_id = :linked_id "
|
||||||
)
|
)
|
||||||
q = cursor.execute(query_str)
|
q = cursor.execute(query_str, {"linked_id": bid_id})
|
||||||
for row in q:
|
for row in q:
|
||||||
events.append(
|
events.append(
|
||||||
{
|
{
|
||||||
|
@ -7183,21 +7188,23 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
def getCompletedAndActiveBidsValue(self, offer, cursor):
|
def getCompletedAndActiveBidsValue(self, offer, cursor):
|
||||||
bids = []
|
bids = []
|
||||||
total_value = 0
|
total_value: int = 0
|
||||||
|
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
"""SELECT bid_id, amount, state FROM bids
|
"""SELECT bid_id, amount, state FROM bids
|
||||||
JOIN bidstates ON bidstates.state_id = bids.state AND (bidstates.state_id = {1} OR bidstates.in_progress > 0)
|
JOIN bidstates ON bidstates.state_id = bids.state AND (bidstates.state_id = :state_id OR bidstates.in_progress > 0)
|
||||||
WHERE bids.active_ind = 1 AND bids.offer_id = x\'{0}\'
|
WHERE bids.active_ind = 1 AND bids.offer_id = :offer_id
|
||||||
UNION
|
UNION
|
||||||
SELECT bid_id, amount, state FROM bids
|
SELECT bid_id, amount, state FROM bids
|
||||||
JOIN actions ON actions.linked_id = bids.bid_id AND actions.active_ind = 1 AND (actions.action_type = {2} OR actions.action_type = {3})
|
JOIN actions ON actions.linked_id = bids.bid_id AND actions.active_ind = 1 AND (actions.action_type = :action_type_acc_bid OR actions.action_type = :action_type_acc_adp_bid)
|
||||||
WHERE bids.active_ind = 1 AND bids.offer_id = x\'{0}\'
|
WHERE bids.active_ind = 1 AND bids.offer_id = :offer_id
|
||||||
""".format(
|
""",
|
||||||
offer.offer_id.hex(),
|
{
|
||||||
BidStates.SWAP_COMPLETED,
|
"state_id": int(BidStates.SWAP_COMPLETED),
|
||||||
ActionTypes.ACCEPT_XMR_BID,
|
"offer_id": offer.offer_id,
|
||||||
ActionTypes.ACCEPT_BID,
|
"action_type_acc_bid": int(ActionTypes.ACCEPT_BID),
|
||||||
)
|
"action_type_acc_adp_bid": int(ActionTypes.ACCEPT_XMR_BID),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
for row in q:
|
for row in q:
|
||||||
bid_id, amount, state = row
|
bid_id, amount, state = row
|
||||||
|
@ -9214,9 +9221,12 @@ class BasicSwap(BaseApp):
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
try:
|
try:
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
"SELECT COUNT(*) FROM xmr_split_data WHERE bid_id = x'{}' AND msg_type = {} AND msg_sequence = {}".format(
|
"SELECT COUNT(*) FROM xmr_split_data WHERE bid_id = :bid_id AND msg_type = :msg_type AND msg_sequence = :msg_sequence",
|
||||||
msg_data.msg_id.hex(), msg_data.msg_type, msg_data.sequence
|
{
|
||||||
)
|
"bid_id": msg_data.msg_id,
|
||||||
|
"msg_type": msg_data.msg_type,
|
||||||
|
"msg_sequence": msg_data.sequence,
|
||||||
|
},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
num_exists = q[0]
|
num_exists = q[0]
|
||||||
if num_exists > 0:
|
if num_exists > 0:
|
||||||
|
@ -10120,37 +10130,39 @@ class BasicSwap(BaseApp):
|
||||||
num_watched_outputs += len(v["watched_outputs"])
|
num_watched_outputs += len(v["watched_outputs"])
|
||||||
|
|
||||||
now: int = self.getTime()
|
now: int = self.getTime()
|
||||||
q_bids_str = """SELECT
|
q_bids_str: str = (
|
||||||
|
"""SELECT
|
||||||
COUNT(CASE WHEN b.was_sent THEN 1 ELSE NULL END) AS count_sent,
|
COUNT(CASE WHEN b.was_sent THEN 1 ELSE NULL END) AS count_sent,
|
||||||
COUNT(CASE WHEN b.was_sent AND (s.in_progress OR (s.swap_ended = 0 AND b.expire_at > {} AND o.expire_at > {})) THEN 1 ELSE NULL END) AS count_sent_active,
|
COUNT(CASE WHEN b.was_sent AND (s.in_progress OR (s.swap_ended = 0 AND b.expire_at > :now AND o.expire_at > :now)) THEN 1 ELSE NULL END) AS count_sent_active,
|
||||||
COUNT(CASE WHEN b.was_received THEN 1 ELSE NULL END) AS count_received,
|
COUNT(CASE WHEN b.was_received THEN 1 ELSE NULL END) AS count_received,
|
||||||
COUNT(CASE WHEN b.was_received AND b.state = {} AND b.expire_at > {} AND o.expire_at > {} THEN 1 ELSE NULL END) AS count_available,
|
COUNT(CASE WHEN b.was_received AND b.state = :received_state AND b.expire_at > :now AND o.expire_at > :now THEN 1 ELSE NULL END) AS count_available,
|
||||||
COUNT(CASE WHEN b.was_received AND (s.in_progress OR (s.swap_ended = 0 AND b.expire_at > {} AND o.expire_at > {})) THEN 1 ELSE NULL END) AS count_recv_active
|
COUNT(CASE WHEN b.was_received AND (s.in_progress OR (s.swap_ended = 0 AND b.expire_at > :now AND o.expire_at > :now)) THEN 1 ELSE NULL END) AS count_recv_active
|
||||||
FROM bids b
|
FROM bids b
|
||||||
JOIN offers o ON b.offer_id = o.offer_id
|
JOIN offers o ON b.offer_id = o.offer_id
|
||||||
JOIN bidstates s ON b.state = s.state_id
|
JOIN bidstates s ON b.state = s.state_id
|
||||||
WHERE b.active_ind = 1""".format(
|
WHERE b.active_ind = 1"""
|
||||||
now, now, BidStates.BID_RECEIVED, now, now, now, now
|
|
||||||
)
|
)
|
||||||
|
|
||||||
q_offers_str = """SELECT
|
q_offers_str: str = (
|
||||||
COUNT(CASE WHEN expire_at > {} THEN 1 ELSE NULL END) AS count_active,
|
"""SELECT
|
||||||
|
COUNT(CASE WHEN expire_at > :now THEN 1 ELSE NULL END) AS count_active,
|
||||||
COUNT(CASE WHEN was_sent THEN 1 ELSE NULL END) AS count_sent,
|
COUNT(CASE WHEN was_sent THEN 1 ELSE NULL END) AS count_sent,
|
||||||
COUNT(CASE WHEN was_sent AND expire_at > {} THEN 1 ELSE NULL END) AS count_sent_active
|
COUNT(CASE WHEN was_sent AND expire_at > :now THEN 1 ELSE NULL END) AS count_sent_active
|
||||||
FROM offers WHERE active_ind = 1""".format(
|
FROM offers WHERE active_ind = 1"""
|
||||||
now, now
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
q = cursor.execute(q_bids_str).fetchone()
|
q = cursor.execute(
|
||||||
|
q_bids_str, {"now": now, "received_state": int(BidStates.BID_RECEIVED)}
|
||||||
|
).fetchone()
|
||||||
bids_sent = q[0]
|
bids_sent = q[0]
|
||||||
bids_sent_active = q[1]
|
bids_sent_active = q[1]
|
||||||
bids_received = q[2]
|
bids_received = q[2]
|
||||||
bids_available = q[3]
|
bids_available = q[3]
|
||||||
bids_recv_active = q[4]
|
bids_recv_active = q[4]
|
||||||
|
|
||||||
q = cursor.execute(q_offers_str).fetchone()
|
q = cursor.execute(q_offers_str, {"now": now}).fetchone()
|
||||||
num_offers = q[0]
|
num_offers = q[0]
|
||||||
num_sent_offers = q[1]
|
num_sent_offers = q[1]
|
||||||
num_sent_active_offers = q[2]
|
num_sent_active_offers = q[2]
|
||||||
|
@ -10257,8 +10269,8 @@ class BasicSwap(BaseApp):
|
||||||
),
|
),
|
||||||
cursor,
|
cursor,
|
||||||
)
|
)
|
||||||
query_str = f"DELETE FROM wallets WHERE (coin_id = {coin_id} AND balance_type = {info_type}) AND record_id NOT IN (SELECT record_id FROM wallets WHERE coin_id = {coin_id} AND balance_type = {info_type} ORDER BY created_at DESC LIMIT 3 )"
|
query_str = "DELETE FROM wallets WHERE (coin_id = :coin_id AND balance_type = :info_type) AND record_id NOT IN (SELECT record_id FROM wallets WHERE coin_id = :coin_id AND balance_type = :info_type ORDER BY created_at DESC LIMIT 3 )"
|
||||||
cursor.execute(query_str)
|
cursor.execute(query_str, {"coin_id": coin_id, "info_type": info_type})
|
||||||
self.commitDB()
|
self.commitDB()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error(f"addWalletInfoRecord {e}")
|
self.log.error(f"addWalletInfoRecord {e}")
|
||||||
|
@ -10326,15 +10338,15 @@ class BasicSwap(BaseApp):
|
||||||
rv = {}
|
rv = {}
|
||||||
try:
|
try:
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
|
query_data: dict = {}
|
||||||
where_str = ""
|
where_str = ""
|
||||||
if opts is not None and "coin_id" in opts:
|
if opts is not None and "coin_id" in opts:
|
||||||
where_str = "WHERE coin_id = {}".format(opts["coin_id"])
|
where_str = "WHERE coin_id = :coin_id"
|
||||||
|
query_data["coin_id"] = opts["coin_id"]
|
||||||
inner_str = f"SELECT coin_id, balance_type, MAX(created_at) as max_created_at FROM wallets {where_str} GROUP BY coin_id, balance_type"
|
inner_str = f"SELECT coin_id, balance_type, MAX(created_at) as max_created_at FROM wallets {where_str} GROUP BY coin_id, balance_type"
|
||||||
query_str = "SELECT a.coin_id, a.balance_type, wallet_data, created_at FROM wallets a, ({}) b WHERE a.coin_id = b.coin_id AND a.balance_type = b.balance_type AND a.created_at = b.max_created_at".format(
|
query_str = f"SELECT a.coin_id, a.balance_type, wallet_data, created_at FROM wallets a, ({inner_str}) b WHERE a.coin_id = b.coin_id AND a.balance_type = b.balance_type AND a.created_at = b.max_created_at"
|
||||||
inner_str
|
|
||||||
)
|
|
||||||
|
|
||||||
q = cursor.execute(query_str)
|
q = cursor.execute(query_str, query_data)
|
||||||
for row in q:
|
for row in q:
|
||||||
coin_id = row[0]
|
coin_id = row[0]
|
||||||
|
|
||||||
|
@ -10350,10 +10362,10 @@ class BasicSwap(BaseApp):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ensure the latest addresses are displayed
|
# Ensure the latest addresses are displayed
|
||||||
|
coin_name: str = chainparams[coin_id]["name"]
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
'SELECT key, value FROM kv_string WHERE key = "receive_addr_{0}" OR key = "stealth_addr_{0}"'.format(
|
"SELECT key, value FROM kv_string WHERE key = ? OR key = ?",
|
||||||
chainparams[coin_id]["name"]
|
(f"receive_addr_{coin_name}", f"stealth_addr_{coin_name}"),
|
||||||
)
|
|
||||||
)
|
)
|
||||||
for row in q:
|
for row in q:
|
||||||
if row[0].startswith("stealth"):
|
if row[0].startswith("stealth"):
|
||||||
|
@ -10388,18 +10400,13 @@ class BasicSwap(BaseApp):
|
||||||
def countAcceptedBids(self, offer_id: bytes = None) -> int:
|
def countAcceptedBids(self, offer_id: bytes = None) -> int:
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
try:
|
try:
|
||||||
|
query: str = "SELECT COUNT(*) FROM bids WHERE state >= :state_ind"
|
||||||
|
query_data: dict = {"state_ind": int(BidStates.BID_ACCEPTED)}
|
||||||
if offer_id:
|
if offer_id:
|
||||||
q = cursor.execute(
|
query += " AND offer_id = :offer_id"
|
||||||
"SELECT COUNT(*) FROM bids WHERE state >= {} AND offer_id = x'{}'".format(
|
query_data["offer_id":offer_id]
|
||||||
BidStates.BID_ACCEPTED, offer_id.hex()
|
|
||||||
)
|
q = cursor.execute(query, query_data).fetchone()
|
||||||
).fetchone()
|
|
||||||
else:
|
|
||||||
q = cursor.execute(
|
|
||||||
"SELECT COUNT(*) FROM bids WHERE state >= {}".format(
|
|
||||||
BidStates.BID_ACCEPTED
|
|
||||||
)
|
|
||||||
).fetchone()
|
|
||||||
return q[0]
|
return q[0]
|
||||||
finally:
|
finally:
|
||||||
self.closeDB(cursor, commit=False)
|
self.closeDB(cursor, commit=False)
|
||||||
|
@ -10410,47 +10417,53 @@ class BasicSwap(BaseApp):
|
||||||
rv = []
|
rv = []
|
||||||
now: int = self.getTime()
|
now: int = self.getTime()
|
||||||
|
|
||||||
query_suffix = ""
|
query_suffix: str = ""
|
||||||
|
query_data: dict = {"now": now}
|
||||||
|
|
||||||
if sent:
|
if sent:
|
||||||
query_suffix += " AND was_sent = 1"
|
query_suffix += " AND was_sent = 1"
|
||||||
|
|
||||||
active_state = filters.get("active", "any")
|
active_state = filters.get("active", "any")
|
||||||
if active_state == "active":
|
if active_state == "active":
|
||||||
query_suffix += f" AND (expire_at > {now} AND active_ind = 1)"
|
query_suffix += " AND (expire_at > :now AND active_ind = 1)"
|
||||||
elif active_state == "expired":
|
elif active_state == "expired":
|
||||||
query_suffix += f" AND expire_at <= {now}"
|
query_suffix += " AND expire_at <= :now"
|
||||||
elif active_state == "revoked":
|
elif active_state == "revoked":
|
||||||
query_suffix += " AND active_ind != 1"
|
query_suffix += " AND active_ind != 1"
|
||||||
else:
|
else:
|
||||||
query_suffix += f" AND (expire_at > {now} AND active_ind = 1)"
|
query_suffix += " AND (expire_at > :now AND active_ind = 1)"
|
||||||
|
|
||||||
filter_offer_id = filters.get("offer_id", None)
|
filter_offer_id = filters.get("offer_id", None)
|
||||||
if filter_offer_id is not None:
|
if filter_offer_id is not None:
|
||||||
query_suffix += f" AND offer_id = x'{filter_offer_id.hex()}'"
|
query_suffix += " AND offer_id = :filter_offer_id"
|
||||||
|
query_data["filter_offer_id"] = filter_offer_id
|
||||||
filter_coin_from = filters.get("coin_from", None)
|
filter_coin_from = filters.get("coin_from", None)
|
||||||
if filter_coin_from and filter_coin_from > -1:
|
if filter_coin_from and filter_coin_from > -1:
|
||||||
query_suffix += f" AND coin_from = {int(filter_coin_from)}"
|
query_suffix += " AND coin_from = :filter_coin_from"
|
||||||
|
query_data["filter_coin_from"] = int(filter_coin_from)
|
||||||
filter_coin_to = filters.get("coin_to", None)
|
filter_coin_to = filters.get("coin_to", None)
|
||||||
if filter_coin_to and filter_coin_to > -1:
|
if filter_coin_to and filter_coin_to > -1:
|
||||||
query_suffix += f" AND coin_to = {int(filter_coin_to)}"
|
query_suffix += " AND coin_to = :filter_coin_to"
|
||||||
|
query_data["filter_coin_to"] = int(filter_coin_to)
|
||||||
|
|
||||||
filter_include_sent = filters.get("include_sent", None)
|
filter_include_sent = filters.get("include_sent", None)
|
||||||
if filter_include_sent is not None and filter_include_sent is not True:
|
if filter_include_sent is not None and filter_include_sent is not True:
|
||||||
query_suffix += " AND was_sent = 0"
|
query_suffix += " AND was_sent = 0"
|
||||||
|
|
||||||
order_dir = filters.get("sort_dir", "desc")
|
query_suffix += getOrderByStr(filters)
|
||||||
order_by = filters.get("sort_by", "created_at")
|
|
||||||
query_suffix += f" ORDER BY {order_by} {order_dir.upper()}"
|
|
||||||
|
|
||||||
limit = filters.get("limit", None)
|
limit = filters.get("limit", None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
query_suffix += f" LIMIT {limit}"
|
query_suffix += " LIMIT :limit"
|
||||||
|
query_data["limit"] = limit
|
||||||
offset = filters.get("offset", None)
|
offset = filters.get("offset", None)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
query_suffix += f" OFFSET {offset}"
|
query_suffix += " OFFSET :offset"
|
||||||
|
query_data["offset"] = offset
|
||||||
|
|
||||||
q = self.query(Offer, cursor, query_suffix=query_suffix)
|
q = self.query(
|
||||||
|
Offer, cursor, query_suffix=query_suffix, extra_query_data=query_data
|
||||||
|
)
|
||||||
for row in q:
|
for row in q:
|
||||||
offer = row
|
offer = row
|
||||||
# Show offers for enabled coins only
|
# Show offers for enabled coins only
|
||||||
|
@ -10465,14 +10478,14 @@ class BasicSwap(BaseApp):
|
||||||
self.closeDB(cursor, commit=False)
|
self.closeDB(cursor, commit=False)
|
||||||
|
|
||||||
def activeBidsQueryStr(
|
def activeBidsQueryStr(
|
||||||
self, now: int, offer_table: str = "offers", bids_table: str = "bids"
|
self, offer_table: str = "offers", bids_table: str = "bids"
|
||||||
) -> str:
|
) -> str:
|
||||||
offers_inset = (
|
offers_inset: str = ""
|
||||||
f" AND {offer_table}.expire_at > {now}" if offer_table != "" else ""
|
if offer_table != "":
|
||||||
)
|
offers_inset = f" AND {offer_table}.expire_at > :now"
|
||||||
|
|
||||||
inactive_states_str = ", ".join([str(int(s)) for s in inactive_states])
|
inactive_states_str = ", ".join([str(int(s)) for s in inactive_states])
|
||||||
return f" ({bids_table}.state NOT IN ({inactive_states_str}) AND ({bids_table}.state > {BidStates.BID_RECEIVED} OR ({bids_table}.expire_at > {now}{offers_inset}))) "
|
return f" ({bids_table}.state NOT IN ({inactive_states_str}) AND ({bids_table}.state > {BidStates.BID_RECEIVED} OR ({bids_table}.expire_at > :now{offers_inset}))) "
|
||||||
|
|
||||||
def listBids(
|
def listBids(
|
||||||
self,
|
self,
|
||||||
|
@ -10486,7 +10499,15 @@ class BasicSwap(BaseApp):
|
||||||
rv = []
|
rv = []
|
||||||
now: int = self.getTime()
|
now: int = self.getTime()
|
||||||
|
|
||||||
query_str = (
|
query_data: dict = {
|
||||||
|
"now": now,
|
||||||
|
"ads_swap": SwapTypes.XMR_SWAP,
|
||||||
|
"itx_type": TxTypes.ITX,
|
||||||
|
"ptx_type": TxTypes.PTX,
|
||||||
|
"al_type": TxTypes.XMR_SWAP_A_LOCK,
|
||||||
|
"bl_type": TxTypes.XMR_SWAP_B_LOCK,
|
||||||
|
}
|
||||||
|
query_str: str = (
|
||||||
"SELECT "
|
"SELECT "
|
||||||
+ "bids.created_at, bids.expire_at, bids.bid_id, bids.offer_id, bids.amount, bids.state, bids.was_received, "
|
+ "bids.created_at, bids.expire_at, bids.bid_id, bids.offer_id, bids.amount, bids.state, bids.was_received, "
|
||||||
+ "tx1.state, tx2.state, offers.coin_from, bids.rate, bids.bid_addr, offers.bid_reversed, bids.amount_to, offers.coin_to "
|
+ "tx1.state, tx2.state, offers.coin_from, bids.rate, bids.bid_addr, offers.bid_reversed, bids.amount_to, offers.coin_to "
|
||||||
|
@ -10499,9 +10520,11 @@ class BasicSwap(BaseApp):
|
||||||
query_str += "WHERE bids.active_ind = 1 "
|
query_str += "WHERE bids.active_ind = 1 "
|
||||||
filter_bid_id = filters.get("bid_id", None)
|
filter_bid_id = filters.get("bid_id", None)
|
||||||
if filter_bid_id is not None:
|
if filter_bid_id is not None:
|
||||||
query_str += "AND bids.bid_id = x'{}' ".format(filter_bid_id.hex())
|
query_str += "AND bids.bid_id = :filter_bid_id "
|
||||||
|
query_data["filter_bid_id"] = filter_bid_id
|
||||||
if offer_id is not None:
|
if offer_id is not None:
|
||||||
query_str += "AND bids.offer_id = x'{}' ".format(offer_id.hex())
|
query_str += "AND bids.offer_id = :filter_offer_id "
|
||||||
|
query_data["filter_offer_id"] = offer_id
|
||||||
elif sent:
|
elif sent:
|
||||||
query_str += "AND bids.was_sent = 1 "
|
query_str += "AND bids.was_sent = 1 "
|
||||||
else:
|
else:
|
||||||
|
@ -10509,41 +10532,31 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
bid_state_ind = filters.get("bid_state_ind", -1)
|
bid_state_ind = filters.get("bid_state_ind", -1)
|
||||||
if bid_state_ind != -1:
|
if bid_state_ind != -1:
|
||||||
query_str += "AND bids.state = {} ".format(bid_state_ind)
|
query_str += "AND bids.state = :bid_state_ind "
|
||||||
|
query_data["bid_state_ind"] = bid_state_ind
|
||||||
|
|
||||||
with_available_or_active = filters.get("with_available_or_active", False)
|
with_available_or_active = filters.get("with_available_or_active", False)
|
||||||
with_expired = filters.get("with_expired", True)
|
with_expired = filters.get("with_expired", True)
|
||||||
if with_available_or_active:
|
if with_available_or_active:
|
||||||
query_str += " AND " + self.activeBidsQueryStr(now)
|
query_str += " AND " + self.activeBidsQueryStr()
|
||||||
else:
|
else:
|
||||||
if with_expired is not True:
|
if with_expired is not True:
|
||||||
query_str += (
|
query_str += (
|
||||||
"AND bids.expire_at > {} AND offers.expire_at > {} ".format(
|
"AND bids.expire_at > :now AND offers.expire_at > :now "
|
||||||
now, now
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
sort_dir = filters.get("sort_dir", "DESC").upper()
|
query_str += getOrderByStr(filters, table_name="bids")
|
||||||
sort_by = filters.get("sort_by", "created_at")
|
|
||||||
query_str += f" ORDER BY bids.{sort_by} {sort_dir}"
|
|
||||||
|
|
||||||
limit = filters.get("limit", None)
|
limit = filters.get("limit", None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
query_str += f" LIMIT {limit}"
|
query_str += " LIMIT :limit"
|
||||||
|
query_data["limit"] = limit
|
||||||
offset = filters.get("offset", None)
|
offset = filters.get("offset", None)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
query_str += f" OFFSET {offset}"
|
query_str += " OFFSET :offset"
|
||||||
|
query_data["offset"] = offset
|
||||||
|
|
||||||
q = cursor.execute(
|
q = cursor.execute(query_str, query_data)
|
||||||
query_str,
|
|
||||||
{
|
|
||||||
"ads_swap": SwapTypes.XMR_SWAP,
|
|
||||||
"itx_type": TxTypes.ITX,
|
|
||||||
"ptx_type": TxTypes.PTX,
|
|
||||||
"al_type": TxTypes.XMR_SWAP_A_LOCK,
|
|
||||||
"bl_type": TxTypes.XMR_SWAP_B_LOCK,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
for row in q:
|
for row in q:
|
||||||
result = [x for x in row]
|
result = [x for x in row]
|
||||||
coin_from = result[9]
|
coin_from = result[9]
|
||||||
|
@ -10604,9 +10617,10 @@ class BasicSwap(BaseApp):
|
||||||
self.mxDB.release()
|
self.mxDB.release()
|
||||||
|
|
||||||
def listAllSMSGAddresses(self, filters={}, cursor=None):
|
def listAllSMSGAddresses(self, filters={}, cursor=None):
|
||||||
query_str = "SELECT addr_id, addr, use_type, active_ind, created_at, note, pubkey FROM smsgaddresses"
|
query_str: str = (
|
||||||
query_str += " WHERE 1 = 1 "
|
"SELECT addr_id, addr, use_type, active_ind, created_at, note, pubkey FROM smsgaddresses WHERE 1=1 "
|
||||||
query_data = {}
|
)
|
||||||
|
query_data: dict = {}
|
||||||
|
|
||||||
if filters.get("exclude_inactive", True) is True:
|
if filters.get("exclude_inactive", True) is True:
|
||||||
query_str += " AND active_ind = :active_ind "
|
query_str += " AND active_ind = :active_ind "
|
||||||
|
@ -10621,15 +10635,15 @@ class BasicSwap(BaseApp):
|
||||||
query_str += " AND use_type = :addr_type "
|
query_str += " AND use_type = :addr_type "
|
||||||
query_data["addr_type"] = filters["addr_type"]
|
query_data["addr_type"] = filters["addr_type"]
|
||||||
|
|
||||||
sort_dir = filters.get("sort_dir", "DESC").upper()
|
query_str += getOrderByStr(filters)
|
||||||
sort_by = filters.get("sort_by", "created_at")
|
|
||||||
query_str += f" ORDER BY {sort_by} {sort_dir}"
|
|
||||||
limit = filters.get("limit", None)
|
limit = filters.get("limit", None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
query_str += f" LIMIT {limit}"
|
query_str += " LIMIT :limit"
|
||||||
|
query_data["limit"] = limit
|
||||||
offset = filters.get("offset", None)
|
offset = filters.get("offset", None)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
query_str += f" OFFSET {offset}"
|
query_str += " OFFSET :offset"
|
||||||
|
query_data["offset"] = offset
|
||||||
|
|
||||||
try:
|
try:
|
||||||
use_cursor = self.openDB(cursor)
|
use_cursor = self.openDB(cursor)
|
||||||
|
@ -10666,9 +10680,8 @@ class BasicSwap(BaseApp):
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
rv = []
|
rv = []
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
"SELECT sa.addr, ki.label FROM smsgaddresses AS sa LEFT JOIN knownidentities AS ki ON sa.addr = ki.address WHERE sa.use_type = {} AND sa.active_ind = 1 ORDER BY sa.addr_id DESC".format(
|
"SELECT sa.addr, ki.label FROM smsgaddresses AS sa LEFT JOIN knownidentities AS ki ON sa.addr = ki.address WHERE sa.use_type = ? AND sa.active_ind = 1 ORDER BY sa.addr_id DESC",
|
||||||
use_type
|
(use_type,),
|
||||||
)
|
|
||||||
)
|
)
|
||||||
for row in q:
|
for row in q:
|
||||||
rv.append((row[0], row[1]))
|
rv.append((row[0], row[1]))
|
||||||
|
@ -10681,25 +10694,29 @@ class BasicSwap(BaseApp):
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
rv = []
|
rv = []
|
||||||
|
|
||||||
query_str = "SELECT strats.record_id, strats.label, strats.type_ind FROM automationstrategies AS strats"
|
query_str: str = (
|
||||||
|
"SELECT strats.record_id, strats.label, strats.type_ind FROM automationstrategies AS strats"
|
||||||
|
)
|
||||||
query_str += " WHERE strats.active_ind = 1 "
|
query_str += " WHERE strats.active_ind = 1 "
|
||||||
|
query_data: dict = {}
|
||||||
|
|
||||||
type_ind = filters.get("type_ind", None)
|
type_ind = filters.get("type_ind", None)
|
||||||
if type_ind is not None:
|
if type_ind is not None:
|
||||||
query_str += f" AND strats.type_ind = {type_ind} "
|
query_str += " AND strats.type_ind = :type_ind "
|
||||||
|
query_data["type_ind"] = type_ind
|
||||||
|
|
||||||
sort_dir = filters.get("sort_dir", "DESC").upper()
|
query_str += getOrderByStr(filters, table_name="strats")
|
||||||
sort_by = filters.get("sort_by", "created_at")
|
|
||||||
query_str += f" ORDER BY strats.{sort_by} {sort_dir}"
|
|
||||||
|
|
||||||
limit = filters.get("limit", None)
|
limit = filters.get("limit", None)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
query_str += f" LIMIT {limit}"
|
query_str += " LIMIT :limit"
|
||||||
|
query_data["limit"] = limit
|
||||||
offset = filters.get("offset", None)
|
offset = filters.get("offset", None)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
query_str += f" OFFSET {offset}"
|
query_str += " OFFSET :offset"
|
||||||
|
query_data["offset"] = offset
|
||||||
|
|
||||||
q = cursor.execute(query_str)
|
q = cursor.execute(query_str, query_data)
|
||||||
for row in q:
|
for row in q:
|
||||||
rv.append(row)
|
rv.append(row)
|
||||||
return rv
|
return rv
|
||||||
|
@ -10733,11 +10750,10 @@ class BasicSwap(BaseApp):
|
||||||
query_str = (
|
query_str = (
|
||||||
"SELECT links.strategy_id, strats.label FROM automationlinks links"
|
"SELECT links.strategy_id, strats.label FROM automationlinks links"
|
||||||
+ " LEFT JOIN automationstrategies strats ON strats.record_id = links.strategy_id"
|
+ " LEFT JOIN automationstrategies strats ON strats.record_id = links.strategy_id"
|
||||||
+ " WHERE links.linked_type = {} AND links.linked_id = x'{}' AND links.active_ind = 1".format(
|
+ " WHERE links.linked_type = :linked_type AND links.linked_id = :linked_id AND links.active_ind = 1"
|
||||||
int(linked_type), linked_id.hex()
|
|
||||||
)
|
)
|
||||||
)
|
query_data: dict = {"linked_type": int(linked_type), "linked_id": linked_id}
|
||||||
q = cursor.execute(query_str).fetchone()
|
q = cursor.execute(query_str, query_data).fetchone()
|
||||||
return q
|
return q
|
||||||
finally:
|
finally:
|
||||||
self.closeDB(cursor, commit=False)
|
self.closeDB(cursor, commit=False)
|
||||||
|
|
|
@ -39,6 +39,35 @@ def firstOrNone(gen):
|
||||||
return all_rows[0] if len(all_rows) > 0 else None
|
return all_rows[0] if len(all_rows) > 0 else None
|
||||||
|
|
||||||
|
|
||||||
|
def validColumnName(name: str) -> bool:
|
||||||
|
if not isinstance(name, str):
|
||||||
|
return False
|
||||||
|
if len(name) < 1:
|
||||||
|
return False
|
||||||
|
# First character must be alpha
|
||||||
|
if not name[0].isalpha():
|
||||||
|
return False
|
||||||
|
# Rest can be alphanumeric or underscores
|
||||||
|
for c in name[1:]:
|
||||||
|
if not c.isalnum() and c != "_":
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def getOrderByStr(
|
||||||
|
filters: dict, default_sort_by: str = "created_at", table_name: str = ""
|
||||||
|
):
|
||||||
|
sort_by = filters.get("sort_by", default_sort_by)
|
||||||
|
if not validColumnName(sort_by):
|
||||||
|
raise ValueError("Invalid sort by")
|
||||||
|
if table_name != "":
|
||||||
|
sort_by = table_name + "." + sort_by
|
||||||
|
sort_dir = filters.get("sort_dir", "DESC").upper()
|
||||||
|
if sort_dir not in ("ASC", "DESC"):
|
||||||
|
raise ValueError("Invalid sort dir")
|
||||||
|
return f" ORDER BY {sort_by} {sort_dir}"
|
||||||
|
|
||||||
|
|
||||||
def pack_state(new_state: int, now: int) -> bytes:
|
def pack_state(new_state: int, now: int) -> bytes:
|
||||||
return int(new_state).to_bytes(4, "little") + now.to_bytes(8, "little")
|
return int(new_state).to_bytes(4, "little") + now.to_bytes(8, "little")
|
||||||
|
|
||||||
|
@ -851,6 +880,8 @@ class DBMethods:
|
||||||
if upsert:
|
if upsert:
|
||||||
query += " ON CONFLICT DO UPDATE SET "
|
query += " ON CONFLICT DO UPDATE SET "
|
||||||
for i, key in enumerate(values):
|
for i, key in enumerate(values):
|
||||||
|
if not validColumnName(key):
|
||||||
|
raise ValueError(f"Invalid column: {key}")
|
||||||
if i > 0:
|
if i > 0:
|
||||||
query += ", "
|
query += ", "
|
||||||
query += f"{key}=:{key}"
|
query += f"{key}=:{key}"
|
||||||
|
@ -858,7 +889,13 @@ class DBMethods:
|
||||||
cursor.execute(query, values)
|
cursor.execute(query, values)
|
||||||
|
|
||||||
def query(
|
def query(
|
||||||
self, table_class, cursor, constraints={}, order_by={}, query_suffix=None
|
self,
|
||||||
|
table_class,
|
||||||
|
cursor,
|
||||||
|
constraints={},
|
||||||
|
order_by={},
|
||||||
|
query_suffix=None,
|
||||||
|
extra_query_data={},
|
||||||
):
|
):
|
||||||
if cursor is None:
|
if cursor is None:
|
||||||
raise ValueError("Cursor is null")
|
raise ValueError("Cursor is null")
|
||||||
|
@ -883,16 +920,25 @@ class DBMethods:
|
||||||
|
|
||||||
query += f" FROM {table_name} WHERE 1=1 "
|
query += f" FROM {table_name} WHERE 1=1 "
|
||||||
|
|
||||||
for ck, cv in constraints.items():
|
for ck in constraints:
|
||||||
|
if not validColumnName(ck):
|
||||||
|
raise ValueError(f"Invalid constraint column: {ck}")
|
||||||
query += f" AND {ck} = :{ck} "
|
query += f" AND {ck} = :{ck} "
|
||||||
|
|
||||||
for order_col, order_dir in order_by.items():
|
for order_col, order_dir in order_by.items():
|
||||||
query += f" ORDER BY {order_col} {order_dir.upper()}"
|
if validColumnName(order_col) is False:
|
||||||
|
raise ValueError(f"Invalid sort by: {order_col}")
|
||||||
|
order_dir = order_dir.upper()
|
||||||
|
if order_dir not in ("ASC", "DESC"):
|
||||||
|
raise ValueError(f"Invalid sort dir: {order_dir}")
|
||||||
|
query += f" ORDER BY {order_col} {order_dir}"
|
||||||
|
|
||||||
if query_suffix:
|
if query_suffix:
|
||||||
query += query_suffix
|
query += query_suffix
|
||||||
|
|
||||||
rows = cursor.execute(query, constraints)
|
query_data = constraints.copy()
|
||||||
|
query_data.update(extra_query_data)
|
||||||
|
rows = cursor.execute(query, query_data)
|
||||||
for row in rows:
|
for row in rows:
|
||||||
obj = table_class()
|
obj = table_class()
|
||||||
for i, column_info in enumerate(columns):
|
for i, column_info in enumerate(columns):
|
||||||
|
|
|
@ -14,14 +14,16 @@ def remove_expired_data(self, time_offset: int = 0):
|
||||||
try:
|
try:
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
|
|
||||||
active_bids_insert = self.activeBidsQueryStr(now, "", "b2")
|
active_bids_insert: str = self.activeBidsQueryStr("", "b2")
|
||||||
query_str = f"""
|
query_str = f"""
|
||||||
SELECT o.offer_id FROM offers o
|
SELECT o.offer_id FROM offers o
|
||||||
WHERE o.expire_at <= :expired_at AND 0 = (SELECT COUNT(*) FROM bids b2 WHERE b2.offer_id = o.offer_id AND {active_bids_insert})
|
WHERE o.expire_at <= :expired_at AND 0 = (SELECT COUNT(*) FROM bids b2 WHERE b2.offer_id = o.offer_id AND {active_bids_insert})
|
||||||
"""
|
"""
|
||||||
num_offers = 0
|
num_offers = 0
|
||||||
num_bids = 0
|
num_bids = 0
|
||||||
offer_rows = cursor.execute(query_str, {"expired_at": now - time_offset})
|
offer_rows = cursor.execute(
|
||||||
|
query_str, {"now": now, "expired_at": now - time_offset}
|
||||||
|
)
|
||||||
for offer_row in offer_rows:
|
for offer_row in offer_rows:
|
||||||
num_offers += 1
|
num_offers += 1
|
||||||
bid_rows = cursor.execute(
|
bid_rows = cursor.execute(
|
||||||
|
|
|
@ -659,13 +659,18 @@ def page_offer(self, url_split, post_string):
|
||||||
extra_options=extra_options,
|
extra_options=extra_options,
|
||||||
).hex()
|
).hex()
|
||||||
|
|
||||||
sent_bid_id = swap_client.postBid(offer_id, amount_from, addr_send_from=addr_from, extra_options=extra_options).hex()
|
sent_bid_id = swap_client.postBid(
|
||||||
|
offer_id,
|
||||||
|
amount_from,
|
||||||
|
addr_send_from=addr_from,
|
||||||
|
extra_options=extra_options,
|
||||||
|
).hex()
|
||||||
|
|
||||||
if debugind > -1:
|
if debugind > -1:
|
||||||
swap_client.setBidDebugInd(bytes.fromhex(sent_bid_id), debugind)
|
swap_client.setBidDebugInd(bytes.fromhex(sent_bid_id), debugind)
|
||||||
|
|
||||||
self.send_response(302)
|
self.send_response(302)
|
||||||
self.send_header('Location', f'/bid/{sent_bid_id}')
|
self.send_header("Location", f"/bid/{sent_bid_id}")
|
||||||
self.end_headers
|
self.end_headers
|
||||||
return bytes
|
return bytes
|
||||||
|
|
||||||
|
|
|
@ -672,9 +672,9 @@ class Test(unittest.TestCase):
|
||||||
"name": "offer should fail",
|
"name": "offer should fail",
|
||||||
"coin_from": "Particl",
|
"coin_from": "Particl",
|
||||||
"coin_to": "XMR",
|
"coin_to": "XMR",
|
||||||
"amount": 200,
|
"amount": 20000,
|
||||||
"minrate": 0.05,
|
"minrate": 0.05,
|
||||||
"ratetweakpercent": 50000000,
|
"ratetweakpercent": 500000000,
|
||||||
"amount_variable": True,
|
"amount_variable": True,
|
||||||
"address": -1,
|
"address": -1,
|
||||||
"min_coin_from_amt": 20,
|
"min_coin_from_amt": 20,
|
||||||
|
|
Loading…
Reference in a new issue