From d909115ea4a8b0f996f6ababb1bdc14d6c6d8490 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Mon, 6 Jun 2022 23:03:31 +0200 Subject: [PATCH] refactor: Rename EventQueue table to Action --- basicswap/basicswap.py | 157 +++++++++++++++++++------------- basicswap/basicswap_util.py | 5 +- basicswap/db.py | 21 +++-- basicswap/db_upgrades.py | 11 ++- basicswap/ui/page_automation.py | 6 +- basicswap/ui/page_offers.py | 6 +- tests/basicswap/common.py | 15 ++- tests/basicswap/test_xmr.py | 37 +++++++- 8 files changed, 172 insertions(+), 86 deletions(-) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index e6eaa66..e18f031 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -77,7 +77,7 @@ from .messages_pb2 import ( ) from .db import ( CURRENT_DB_VERSION, - TableTypes, + Concepts, Base, DBKVInt, DBKVString, @@ -87,7 +87,7 @@ from .db import ( PooledAddress, SentOffer, SmsgAddress, - EventQueue, + Action, EventLog, XmrOffer, XmrSwap, @@ -117,7 +117,7 @@ from .basicswap_util import ( BidStates, TxStates, TxTypes, - EventTypes, + ActionTypes, EventLogTypes, XmrSplitMsgTypes, DebugTypes, @@ -205,14 +205,14 @@ class BasicSwap(BaseApp): self.check_progress_seconds = self.settings.get('check_progress_seconds', 60) self.check_watched_seconds = self.settings.get('check_watched_seconds', 60) self.check_expired_seconds = self.settings.get('check_expired_seconds', 60 * 5) - self.check_events_seconds = self.settings.get('check_events_seconds', 10) + self.check_actions_seconds = self.settings.get('check_actions_seconds', 10) self.check_xmr_swaps_seconds = self.settings.get('check_xmr_swaps_seconds', 20) self.startup_tries = self.settings.get('startup_tries', 21) # Seconds waited for will be (x(1 + x+1) / 2 self.debug_ui = self.settings.get('debug_ui', False) self._last_checked_progress = 0 self._last_checked_watched = 0 self._last_checked_expired = 0 - self._last_checked_events = 0 + self._last_checked_actions = 0 self._last_checked_xmr_swaps = 0 self._possibly_revoked_offers = collections.deque([], maxlen=48) # TODO: improve self._updating_wallets_info = {} @@ -793,9 +793,9 @@ class BasicSwap(BaseApp): # Remove any delayed events if self.debug: - use_session.execute('UPDATE eventqueue SET active_ind = 2 WHERE linked_id = x\'{}\' '.format(bid.bid_id.hex())) + use_session.execute('UPDATE actions SET active_ind = 2 WHERE linked_id = x\'{}\' '.format(bid.bid_id.hex())) else: - use_session.execute('DELETE FROM eventqueue WHERE linked_id = x\'{}\' '.format(bid.bid_id.hex())) + use_session.execute('DELETE FROM actions WHERE linked_id = x\'{}\' '.format(bid.bid_id.hex())) # Unlock locked inputs (TODO) if offer.swap_type == SwapTypes.XMR_SWAP: @@ -1090,7 +1090,7 @@ class BasicSwap(BaseApp): if automation_id != -1: auto_link = AutomationLink( active_ind=1, - linked_type=TableTypes.OFFER, + linked_type=Concepts.OFFER, linked_id=offer_id, strategy_id=automation_id, created_at=offer_created_at, @@ -1549,36 +1549,35 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() - def createEventInSession(self, delay, event_type, linked_id, session): - self.log.debug('createEvent %d %s', event_type, linked_id.hex()) + def createActionInSession(self, delay, action_type, linked_id, session): + self.log.debug('createAction %d %s', action_type, linked_id.hex()) now = int(time.time()) - event = EventQueue( + action = Action( active_ind=1, created_at=now, trigger_at=now + delay, - event_type=event_type, + action_type=action_type, linked_id=linked_id) - session.add(event) + session.add(action) - def createEvent(self, delay, event_type, linked_id): - # self.log.debug('createEvent %d %s', event_type, linked_id.hex()) + def createAction(self, delay, action_type, linked_id): + # self.log.debug('createAction %d %s', action_type, linked_id.hex()) self.mxDB.acquire() try: session = scoped_session(self.session_factory) - self.createEventInSession(delay, event_type, linked_id, session) + self.createActionInSession(delay, action_type, linked_id, session) session.commit() finally: session.close() session.remove() self.mxDB.release() - def logBidEvent(self, bid_id, event_type, event_msg, session): - self.log.debug('logBidEvent %s %s', bid_id.hex(), event_type) + def logEvent(self, linked_type, linked_id, event_type, event_msg, session): entry = EventLog( active_ind=1, created_at=int(time.time()), - linked_type=TableTypes.BID, - linked_id=bid_id, + linked_type=linked_type, + linked_id=linked_id, event_type=int(event_type), event_msg=event_msg) @@ -1595,10 +1594,27 @@ class BasicSwap(BaseApp): session.remove() self.mxDB.release() + def logBidEvent(self, bid_id, event_type, event_msg, session): + self.log.debug('logBidEvent %s %s', bid_id.hex(), event_type) + self.logEvent(Concepts.BID, bid_id, event_type, event_msg, session) + def countBidEvents(self, bid, event_type, session): - q = session.execute('SELECT COUNT(*) FROM eventlog WHERE linked_type = {} AND linked_id = x\'{}\' AND event_type = {}'.format(int(TableTypes.BID), bid.bid_id.hex(), int(event_type))).first() + q = session.execute('SELECT COUNT(*) FROM eventlog WHERE linked_type = {} AND linked_id = x\'{}\' AND event_type = {}'.format(int(Concepts.BID), bid.bid_id.hex(), int(event_type))).first() return q[0] + def getEvents(self, linked_type, linked_id): + events = [] + self.mxDB.acquire() + try: + session = scoped_session(self.session_factory) + for entry in session.query(EventLog).filter(sa.and_(EventLog.linked_type == linked_type, EventLog.linked_id == linked_id)): + events.append(entry) + return events + finally: + session.close() + session.remove() + self.mxDB.release() + def postBid(self, offer_id, amount, addr_send_from=None, extra_options={}): # Bid to send bid.amount * bid.rate of coin_to in exchange for bid.amount of coin_from self.log.debug('postBid %s', offer_id.hex()) @@ -1838,13 +1854,13 @@ class BasicSwap(BaseApp): def list_bid_events(self, bid_id, session): query_str = 'SELECT created_at, event_type, event_msg FROM eventlog ' + \ - 'WHERE active_ind = 1 AND linked_type = {} AND linked_id = x\'{}\' '.format(TableTypes.BID, bid_id.hex()) + 'WHERE active_ind = 1 AND linked_type = {} AND linked_id = x\'{}\' '.format(Concepts.BID, bid_id.hex()) q = session.execute(query_str) events = [] for row in q: events.append({'at': row[0], 'desc': describeEventEntry(row[1], row[2])}) - query_str = 'SELECT created_at, trigger_at FROM eventqueue ' + \ + query_str = 'SELECT created_at, trigger_at FROM actions ' + \ 'WHERE active_ind = 1 AND linked_id = x\'{}\' '.format(bid_id.hex()) q = session.execute(query_str) for row in q: @@ -1962,21 +1978,21 @@ class BasicSwap(BaseApp): ensure(xmr_offer, 'XMR offer not found: {}.'.format(offer_id.hex())) ensure(offer.expire_at > int(time.time()), 'Offer has expired') - valid_for_seconds = extra_options.get('valid_for_seconds', 60 * 10) - self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds) - coin_from = Coins(offer.coin_from) coin_to = Coins(offer.coin_to) ci_from = self.ci(coin_from) ci_to = self.ci(coin_to) + valid_for_seconds = extra_options.get('valid_for_seconds', 60 * 10) bid_rate = extra_options.get('bid_rate', offer.rate) - self.validateBidAmount(offer, amount, bid_rate) + amount_to = int((int(amount) * bid_rate) // ci_from.COIN()) + + if not (self.debug and extra_options.get('debug_skip_validation', False)): + self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds) + self.validateBidAmount(offer, amount, bid_rate) self.checkSynced(coin_from, coin_to) - amount_to = int((int(amount) * bid_rate) // ci_from.COIN()) - balance_to = ci_to.getSpendableBalance() ensure(balance_to > amount_to, '{} spendable balance is too low: {}'.format(ci_to.coin_name(), ci_to.format_amount(balance_to))) @@ -2954,7 +2970,7 @@ class BasicSwap(BaseApp): if bid.was_sent: delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Sending xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.SEND_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.SEND_XMR_SWAP_LOCK_TX_B, bid_id, session) # bid.setState(BidStates.SWAP_DELAYING) if bid_changed: @@ -3001,7 +3017,7 @@ class BasicSwap(BaseApp): if bid.was_received: delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Releasing xmr script coin lock tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.SEND_XMR_LOCK_RELEASE, bid_id, session) + self.createActionInSession(delay, ActionTypes.SEND_XMR_LOCK_RELEASE, bid_id, session) if bid_changed: self.saveBidInSession(bid_id, bid, session, xmr_swap) @@ -3018,11 +3034,11 @@ class BasicSwap(BaseApp): except Exception as e: self.log.debug('getrawtransaction lock spend tx failed: %s', str(e)) elif state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED: - if bid.was_received and self.countQueuedEvents(session, bid_id, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B) < 1: + if bid.was_received and self.countQueuedActions(session, bid_id, ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_B) < 1: bid.setState(BidStates.SWAP_DELAYING) delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Redeeming coin b lock tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session) self.saveBidInSession(bid_id, bid, session, xmr_swap) session.commit() elif state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED: @@ -3411,7 +3427,7 @@ class BasicSwap(BaseApp): if bid.xmr_b_lock_tx is not None: delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Recovering xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session) else: bid.setState(BidStates.XMR_SWAP_FAILED_REFUNDED) @@ -3524,49 +3540,49 @@ class BasicSwap(BaseApp): finally: self.mxDB.release() - def countQueuedEvents(self, session, bid_id, event_type): - q = session.query(EventQueue).filter(sa.and_(EventQueue.active_ind == 1, EventQueue.linked_id == bid_id, EventQueue.event_type == event_type)) + def countQueuedActions(self, session, bid_id, action_type): + q = session.query(Action).filter(sa.and_(Action.active_ind == 1, Action.linked_id == bid_id, Action.action_type == action_type)) return q.count() - def checkEvents(self): + def checkQueuedActions(self): self.mxDB.acquire() now = int(time.time()) session = None try: session = scoped_session(self.session_factory) - q = session.query(EventQueue).filter(sa.and_(EventQueue.active_ind == 1, EventQueue.trigger_at <= now)) + q = session.query(Action).filter(sa.and_(Action.active_ind == 1, Action.trigger_at <= now)) for row in q: try: - if row.event_type == EventTypes.ACCEPT_BID: + if row.action_type == ActionTypes.ACCEPT_BID: self.acceptBid(row.linked_id) - elif row.event_type == EventTypes.ACCEPT_XMR_BID: + elif row.action_type == ActionTypes.ACCEPT_XMR_BID: self.acceptXmrBid(row.linked_id) - elif row.event_type == EventTypes.SIGN_XMR_SWAP_LOCK_TX_A: + elif row.action_type == ActionTypes.SIGN_XMR_SWAP_LOCK_TX_A: self.sendXmrBidTxnSigsFtoL(row.linked_id, session) - elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_A: + elif row.action_type == ActionTypes.SEND_XMR_SWAP_LOCK_TX_A: self.sendXmrBidCoinALockTx(row.linked_id, session) - elif row.event_type == EventTypes.SEND_XMR_SWAP_LOCK_TX_B: + elif row.action_type == ActionTypes.SEND_XMR_SWAP_LOCK_TX_B: self.sendXmrBidCoinBLockTx(row.linked_id, session) - elif row.event_type == EventTypes.SEND_XMR_LOCK_RELEASE: + elif row.action_type == ActionTypes.SEND_XMR_LOCK_RELEASE: self.sendXmrBidLockRelease(row.linked_id, session) - elif row.event_type == EventTypes.REDEEM_XMR_SWAP_LOCK_TX_A: + elif row.action_type == ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_A: self.redeemXmrBidCoinALockTx(row.linked_id, session) - elif row.event_type == EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B: + elif row.action_type == ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_B: self.redeemXmrBidCoinBLockTx(row.linked_id, session) - elif row.event_type == EventTypes.RECOVER_XMR_SWAP_LOCK_TX_B: + elif row.action_type == ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B: self.recoverXmrBidCoinBLockTx(row.linked_id, session) else: self.log.warning('Unknown event type: %d', row.event_type) except Exception as ex: if self.debug: self.log.error(traceback.format_exc()) - self.log.error('checkEvents failed: {}'.format(str(ex))) + self.log.error('checkQueuedActions failed: {}'.format(str(ex))) if self.debug: - session.execute('UPDATE eventqueue SET active_ind = 2 WHERE trigger_at <= {}'.format(now)) + session.execute('UPDATE actions SET active_ind = 2 WHERE trigger_at <= {}'.format(now)) else: - session.execute('DELETE FROM eventqueue WHERE trigger_at <= {}'.format(now)) + session.execute('DELETE FROM actions WHERE trigger_at <= {}'.format(now)) session.commit() finally: @@ -3778,7 +3794,7 @@ class BasicSwap(BaseApp): self.mxDB.acquire() use_session = scoped_session(self.session_factory) - link = use_session.query(AutomationLink).filter_by(active_ind=1, linked_type=TableTypes.OFFER, linked_id=offer.offer_id).first() + link = use_session.query(AutomationLink).filter_by(active_ind=1, linked_type=Concepts.OFFER, linked_id=offer.offer_id).first() if not link: return False @@ -3787,11 +3803,20 @@ class BasicSwap(BaseApp): self.log.debug('Evaluating against strategy {}'.format(strategy.record_id)) - if opts.get('full_amount_only', False) is True: + if not offer.amount_negotiable: if bid.amount != offer.amount_from: self.log.info('Not auto accepting bid %s, want exact amount match', bid.bid_id.hex()) return False + if bid.amount < offer.min_bid_amount: + self.log.info('Not auto accepting bid %s, bid amount below minimum', bid.bid_id.hex()) + return False + + if opts.get('exact_rate_only', False) is True: + if bid.rate != offer.rate: + self.log.info('Not auto accepting bid %s, want exact rate match', bid.bid_id.hex()) + return False + max_bids = opts.get('max_bids', 1) # Auto accept bid if set and no other non-abandoned bid for this order exists if self.countAcceptedBids(offer.offer_id) >= max_bids: @@ -3909,7 +3934,7 @@ class BasicSwap(BaseApp): if self.shouldAutoAcceptBid(offer, bid): delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Auto accepting bid %s in %d seconds', bid_id.hex(), delay) - self.createEvent(delay, EventTypes.ACCEPT_BID, bid_id) + self.createAction(delay, ActionTypes.ACCEPT_BID, bid_id) def processBidAccept(self, msg): self.log.debug('Processing bid accepted msg %s', msg['msgid']) @@ -4029,7 +4054,7 @@ class BasicSwap(BaseApp): if self.shouldAutoAcceptBid(offer, bid, session): delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Auto accepting xmr bid %s in %d seconds', bid.bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.ACCEPT_XMR_BID, bid.bid_id, session) + self.createActionInSession(delay, ActionTypes.ACCEPT_XMR_BID, bid.bid_id, session) bid.setState(BidStates.SWAP_DELAYING) self.saveBidInSession(bid.bid_id, bid, session, xmr_swap) @@ -4087,7 +4112,7 @@ class BasicSwap(BaseApp): delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Responding to xmr bid accept %s in %d seconds', bid.bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.SIGN_XMR_SWAP_LOCK_TX_A, bid.bid_id, session) + self.createActionInSession(delay, ActionTypes.SIGN_XMR_SWAP_LOCK_TX_A, bid.bid_id, session) def processXmrBid(self, msg): # MSG1L @@ -4437,7 +4462,7 @@ class BasicSwap(BaseApp): if num_retries < 5 and (ci_to.is_transient_error(ex) or self.is_transient_error(ex)): delay = random.randrange(self.min_delay_retry, self.max_delay_retry) self.log.info('Retrying sending xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.SEND_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.SEND_XMR_SWAP_LOCK_TX_B, bid_id, session) else: self.setBidError(bid_id, bid, 'publishBLockTx failed: ' + str(ex), save_bid=False) self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer) @@ -4585,7 +4610,7 @@ class BasicSwap(BaseApp): if num_retries < 100 and (ci_to.is_transient_error(ex) or self.is_transient_error(ex)): delay = random.randrange(self.min_delay_retry, self.max_delay_retry) self.log.info('Retrying sending xmr swap chain B spend tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session) else: self.setBidError(bid_id, bid, 'spendBLockTx failed: ' + str(ex), save_bid=False) self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer) @@ -4644,7 +4669,7 @@ class BasicSwap(BaseApp): if num_retries < 100 and (ci_to.is_transient_error(ex) or self.is_transient_error(ex)): delay = random.randrange(self.min_delay_retry, self.max_delay_retry) self.log.info('Retrying sending xmr swap chain B refund tx for bid %s in %d seconds', bid_id.hex(), delay) - self.createEventInSession(delay, EventTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session) + self.createActionInSession(delay, ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session) else: self.setBidError(bid_id, bid, 'spendBLockTx for refund failed: ' + str(ex), save_bid=False) self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer) @@ -4710,7 +4735,7 @@ class BasicSwap(BaseApp): delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Sending coin A lock tx for xmr bid %s in %d seconds', bid_id.hex(), delay) - self.createEvent(delay, EventTypes.SEND_XMR_SWAP_LOCK_TX_A, bid_id) + self.createAction(delay, ActionTypes.SEND_XMR_SWAP_LOCK_TX_A, bid_id) bid.setState(BidStates.SWAP_DELAYING) self.saveBid(bid_id, bid, xmr_swap=xmr_swap) @@ -4829,7 +4854,7 @@ class BasicSwap(BaseApp): delay = random.randrange(self.min_delay_event, self.max_delay_event) self.log.info('Redeeming coin A lock tx for xmr bid %s in %d seconds', bid_id.hex(), delay) - self.createEvent(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_A, bid_id) + self.createAction(delay, ActionTypes.REDEEM_XMR_SWAP_LOCK_TX_A, bid_id) bid.setState(BidStates.XMR_SWAP_LOCK_RELEASED) self.saveBid(bid_id, bid, xmr_swap=xmr_swap) @@ -4866,6 +4891,12 @@ class BasicSwap(BaseApp): self.log.error('processMsg %s', str(ex)) if self.debug: self.log.error(traceback.format_exc()) + self.logEvent(Concepts.NETWORK_MESSAGE, + bytes.fromhex(msg['msgid']), + EventLogTypes.ERROR, + str(ex), + None) + finally: self.mxDB.release() @@ -4940,9 +4971,9 @@ class BasicSwap(BaseApp): self.expireMessages() self._last_checked_expired = now - if now - self._last_checked_events >= self.check_events_seconds: - self.checkEvents() - self._last_checked_events = now + if now - self._last_checked_actions >= self.check_actions_seconds: + self.checkQueuedActions() + self._last_checked_actions = now if now - self._last_checked_xmr_swaps >= self.check_xmr_swaps_seconds: self.checkXmrSwaps() diff --git a/basicswap/basicswap_util.py b/basicswap/basicswap_util.py index 1615c2d..8fd068b 100644 --- a/basicswap/basicswap_util.py +++ b/basicswap/basicswap_util.py @@ -122,7 +122,7 @@ class TxTypes(IntEnum): XMR_SWAP_B_LOCK = auto() -class EventTypes(IntEnum): +class ActionTypes(IntEnum): ACCEPT_BID = auto() ACCEPT_XMR_BID = auto() SIGN_XMR_SWAP_LOCK_TX_A = auto() @@ -155,6 +155,7 @@ class EventLogTypes(IntEnum): LOCK_TX_B_SPEND_TX_PUBLISHED = auto() LOCK_TX_A_REFUND_TX_SEEN = auto() LOCK_TX_A_REFUND_SPEND_TX_SEEN = auto() + ERROR = auto() class XmrSplitMsgTypes(IntEnum): @@ -331,6 +332,8 @@ def describeEventEntry(event_type, event_msg): return 'Lock tx A refund spend tx seen in chain' if event_type == EventLogTypes.SYSTEM_WARNING: return 'Warning: ' + event_msg + if event_type == EventLogTypes.ERROR: + return 'Error: ' + event_msg def getVoutByAddress(txjs, p2sh): diff --git a/basicswap/db.py b/basicswap/db.py index 14d946a..719c303 100644 --- a/basicswap/db.py +++ b/basicswap/db.py @@ -17,16 +17,19 @@ CURRENT_DB_DATA_VERSION = 1 Base = declarative_base() -class TableTypes(IntEnum): +class Concepts(IntEnum): OFFER = auto() BID = auto() + NETWORK_MESSAGE = auto() -def strTableTypes(state): - if state == TableTypes.OFFER: +def strConcepts(state): + if state == Concepts.OFFER: return 'Offer' - if state == TableTypes.BID: + if state == Concepts.BID: return 'Bid' + if state == Concepts.NETWORK_MESSAGE: + return 'Network Message' return 'Unknown' @@ -245,16 +248,16 @@ class SmsgAddress(Base): note = sa.Column(sa.String) -class EventQueue(Base): - __tablename__ = 'eventqueue' +class Action(Base): + __tablename__ = 'actions' - event_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) + action_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) active_ind = sa.Column(sa.Integer) created_at = sa.Column(sa.BigInteger) trigger_at = sa.Column(sa.BigInteger) linked_id = sa.Column(sa.LargeBinary) - event_type = sa.Column(sa.Integer) - event_data = sa.Column(sa.LargeBinary) + action_type = sa.Column(sa.Integer) + action_data = sa.Column(sa.LargeBinary) class EventLog(Base): diff --git a/basicswap/db_upgrades.py b/basicswap/db_upgrades.py index 74fc308..21bed6a 100644 --- a/basicswap/db_upgrades.py +++ b/basicswap/db_upgrades.py @@ -8,7 +8,7 @@ import json from sqlalchemy.orm import scoped_session from .db import ( - TableTypes, + Concepts, AutomationStrategy, CURRENT_DB_VERSION, CURRENT_DB_DATA_VERSION) @@ -27,15 +27,15 @@ def upgradeDatabaseData(self, data_version): session.add(AutomationStrategy( active_ind=1, label='Accept All', - type_ind=TableTypes.OFFER, - data=json.dumps({'full_amount_only': True, + type_ind=Concepts.OFFER, + data=json.dumps({'exact_rate_only': True, 'max_bids': 1}).encode('utf-8'), only_known_identities=False)) session.add(AutomationStrategy( active_ind=1, label='Accept Known', - type_ind=TableTypes.OFFER, - data=json.dumps({'full_amount_only': True, + type_ind=Concepts.OFFER, + data=json.dumps({'exact_rate_only': True, 'max_bids': 1}).encode('utf-8'), only_known_identities=True, note='Accept bids from identities with previously successful swaps only')) @@ -168,6 +168,7 @@ def upgradeDatabase(self, db_version): session.execute('ALTER TABLE wallets ADD COLUMN active_ind INTEGER') session.execute('ALTER TABLE knownidentities ADD COLUMN active_ind INTEGER') + session.execute('ALTER TABLE eventqueue RENAME TO actions') if current_version != db_version: self.db_version = db_version diff --git a/basicswap/ui/page_automation.py b/basicswap/ui/page_automation.py index 5a0bb94..9e22cea 100644 --- a/basicswap/ui/page_automation.py +++ b/basicswap/ui/page_automation.py @@ -16,7 +16,7 @@ from basicswap.util import ( ensure, ) from basicswap.db import ( - strTableTypes, + strConcepts, ) @@ -48,7 +48,7 @@ def page_automation_strategies(self, url_split, post_string): formatted_strategies = [] for s in swap_client.listAutomationStrategies(filters): - formatted_strategies.append((s[0], s[1], strTableTypes(s[2]))) + formatted_strategies.append((s[0], s[1], strConcepts(s[2]))) template = server.env.get_template('automation_strategies.html') return bytes(template.render( @@ -93,7 +93,7 @@ def page_automation_strategy(self, url_split, post_string): formatted_strategy = { 'label': strategy.label, - 'type': strTableTypes(strategy.type_ind), + 'type': strConcepts(strategy.type_ind), 'only_known_identities': 'True' if strategy.only_known_identities is True else 'False', 'data': strategy.data, 'note': strategy.note, diff --git a/basicswap/ui/page_offers.py b/basicswap/ui/page_offers.py index e62b549..3b0537c 100644 --- a/basicswap/ui/page_offers.py +++ b/basicswap/ui/page_offers.py @@ -19,7 +19,7 @@ from .util import ( set_pagination_filters, ) from basicswap.db import ( - TableTypes, + Concepts, ) from basicswap.util import ( ensure, @@ -305,7 +305,7 @@ def page_newoffer(self, url_split, post_string): automation_filters = {} automation_filters['sort_by'] = 'label' - automation_filters['type_ind'] = TableTypes.OFFER + automation_filters['type_ind'] = Concepts.OFFER automation_strategies = swap_client.listAutomationStrategies(automation_filters) return bytes(template.render( @@ -454,7 +454,7 @@ def page_offer(self, url_split, post_string): if offer.was_sent: try: - strategy = swap_client.getLinkedStrategy(TableTypes.OFFER, offer_id) + strategy = swap_client.getLinkedStrategy(Concepts.OFFER, offer_id) data['automation_strat_id'] = strategy[0] data['automation_strat_label'] = strategy[1] except Exception: diff --git a/tests/basicswap/common.py b/tests/basicswap/common.py index 0dcf5ad..ac6b292 100644 --- a/tests/basicswap/common.py +++ b/tests/basicswap/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (c) 2020-2021 tecnovert +# Copyright (c) 2020-2022 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. @@ -140,6 +140,19 @@ def wait_for_bid_tx_state(delay_event, swap_client, bid_id, initiate_state, part raise ValueError('wait_for_bid_tx_state timed out.') +def wait_for_event(delay_event, swap_client, linked_type, linked_id, wait_for=20): + logging.info('wait_for_event') + + for i in range(wait_for): + if delay_event.is_set(): + raise ValueError('Test stopped.') + delay_event.wait(1) + rv = swap_client.getEvents(linked_type, linked_id) + if len(rv) > 0: + return rv + raise ValueError('wait_for_event timed out.') + + def wait_for_offer(delay_event, swap_client, offer_id, wait_for=20): logging.info('wait_for_offer %s', offer_id.hex()) for i in range(wait_for): diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index 4e16429..c3b3bc6 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -19,6 +19,9 @@ import subprocess from urllib.request import urlopen import basicswap.config as cfg +from basicswap.db import ( + Concepts, +) from basicswap.basicswap import ( BasicSwap, Coins, @@ -61,6 +64,7 @@ from tests.basicswap.common import ( checkForks, stopDaemons, wait_for_bid, + wait_for_event, wait_for_offer, wait_for_no_offer, wait_for_none_active, @@ -845,7 +849,9 @@ class Test(BaseTest): def test_09_auto_accept(self): logging.info('---------- Test BTC to XMR auto accept') swap_clients = self.swap_clients - offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 11 * COIN, 101 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP, auto_accept_bids=True) + amt_swap = make_int(random.uniform(0.01, 11.0), scale=8, r=1) + rate_swap = make_int(random.uniform(10.0, 101.0), scale=12, r=1) + offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP, auto_accept_bids=True) wait_for_offer(test_delay_event, swap_clients[1], offer_id) offer = swap_clients[1].listOffers(filters={'offer_id': offer_id})[0] @@ -853,6 +859,35 @@ class Test(BaseTest): wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180) wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True) + def test_09_1_auto_accept_multiple(self): + logging.info('---------- Test BTC to XMR auto accept multiple bids') + swap_clients = self.swap_clients + amt_swap = make_int(10, scale=8, r=1) + rate_swap = make_int(100, scale=12, r=1) + min_bid = make_int(1, scale=8, r=1) + + extra_options = { + 'amount_negotiable': True, + 'automation_id': 1, + } + offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, amt_swap, rate_swap, min_bid, SwapTypes.XMR_SWAP, extra_options=extra_options) + wait_for_offer(test_delay_event, swap_clients[1], offer_id) + offer = swap_clients[1].listOffers(filters={'offer_id': offer_id})[0] + + below_min_bid = min_bid - 1 + try: + bid_id = swap_clients[1].postBid(offer_id, below_min_bid) + except Exception as e: + assert('Bid amount below minimum' in str(e)) + extra_bid_options = { + 'debug_skip_validation': True, + } + bid_id = swap_clients[1].postBid(offer_id, below_min_bid, extra_options=extra_bid_options) + + events = wait_for_event(test_delay_event, swap_clients[0], Concepts.NETWORK_MESSAGE, bid_id) + assert('Bid amount below minimum' in events[0].event_msg) + # TODO + def test_10_locked_refundtx(self): logging.info('---------- Test Refund tx is locked') swap_clients = self.swap_clients