Set bid and offer states when expired.
This commit is contained in:
		
							parent
							
								
									18d8dfd3fc
								
							
						
					
					
						commit
						d6535dbc1d
					
				@ -104,6 +104,7 @@ from .db import (
 | 
			
		||||
    AutomationLink,
 | 
			
		||||
    AutomationStrategy,
 | 
			
		||||
    MessageLink,
 | 
			
		||||
    pack_state,
 | 
			
		||||
)
 | 
			
		||||
from .db_upgrades import upgradeDatabase, upgradeDatabaseData
 | 
			
		||||
from .base import BaseApp
 | 
			
		||||
@ -230,26 +231,30 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        self._version = struct.pack('>HHH', int(v[0]), int(v[1]), int(v[2]))
 | 
			
		||||
 | 
			
		||||
        self._transient_instance = transient_instance
 | 
			
		||||
        self.check_progress_seconds = self.get_int_setting('check_progress_seconds', 60, 1, 10 * 60)
 | 
			
		||||
        self.check_watched_seconds = self.get_int_setting('check_watched_seconds', 60, 1, 10 * 60)
 | 
			
		||||
        self.check_expired_seconds = self.get_int_setting('check_expired_seconds', 5 * 60, 1, 10 * 60)
 | 
			
		||||
        self.check_actions_seconds = self.get_int_setting('check_actions_seconds', 10, 1, 10 * 60)
 | 
			
		||||
        self.check_expired_seconds = self.get_int_setting('check_expired_seconds', 5 * 60, 1, 10 * 60)  # Expire DB records and smsg messages
 | 
			
		||||
        self.check_expiring_bids_offers_seconds = self.get_int_setting('check_expiring_bids_offers_seconds', 60, 1, 10 * 60)  # Set offer and bid states to expired
 | 
			
		||||
        self.check_progress_seconds = self.get_int_setting('check_progress_seconds', 60, 1, 10 * 60)
 | 
			
		||||
        self.check_smsg_seconds = self.get_int_setting('check_smsg_seconds', 10, 1, 10 * 60)
 | 
			
		||||
        self.check_watched_seconds = self.get_int_setting('check_watched_seconds', 60, 1, 10 * 60)
 | 
			
		||||
        self.check_xmr_swaps_seconds = self.get_int_setting('check_xmr_swaps_seconds', 20, 1, 10 * 60)
 | 
			
		||||
        self.startup_tries = self.get_int_setting('startup_tries', 21, 1, 100)  # Seconds waited for will be (x(1 + x+1) / 2
 | 
			
		||||
        self.debug_ui = self.settings.get('debug_ui', False)
 | 
			
		||||
        self._debug_cases = []
 | 
			
		||||
        self._last_checked_progress = 0
 | 
			
		||||
        self._last_checked_watched = 0
 | 
			
		||||
        self._last_checked_expired = 0
 | 
			
		||||
        self._last_checked_actions = 0
 | 
			
		||||
        self._last_checked_expired = 0
 | 
			
		||||
        self._last_checked_expiring_bids_offers = 0
 | 
			
		||||
        self._last_checked_progress = 0
 | 
			
		||||
        self._last_checked_smsg = 0
 | 
			
		||||
        self._last_checked_watched = 0
 | 
			
		||||
        self._last_checked_xmr_swaps = 0
 | 
			
		||||
        self._possibly_revoked_offers = collections.deque([], maxlen=48)  # TODO: improve
 | 
			
		||||
        self._expiring_bids = []  # List of bids expiring soon
 | 
			
		||||
        self._expiring_offers = []  # List of offers expiring soon
 | 
			
		||||
        self._updating_wallets_info = {}
 | 
			
		||||
        self._last_updated_wallets_info = 0
 | 
			
		||||
        self._zmq_queue_enabled = self.settings.get('zmq_queue_enabled', True)
 | 
			
		||||
        self._poll_smsg = self.settings.get('poll_smsg', False)
 | 
			
		||||
        self.check_smsg_seconds = self.get_int_setting('check_smsg_seconds', 10, 1, 10 * 60)
 | 
			
		||||
        self._last_checked_smsg = 0
 | 
			
		||||
 | 
			
		||||
        self._notifications_enabled = self.settings.get('notifications_enabled', True)
 | 
			
		||||
        self._disabled_notification_types = self.settings.get('disabled_notification_types', [])
 | 
			
		||||
@ -6173,7 +6178,88 @@ class BasicSwap(BaseApp):
 | 
			
		||||
 | 
			
		||||
        self.processMsg(msg)
 | 
			
		||||
 | 
			
		||||
    def expireBidsAndOffers(self, now) -> None:
 | 
			
		||||
        bids_to_expire = set()
 | 
			
		||||
        offers_to_expire = set()
 | 
			
		||||
        check_records: bool = False
 | 
			
		||||
 | 
			
		||||
        for i, (bid_id, expired_at) in enumerate(self._expiring_bids):
 | 
			
		||||
            if expired_at <= now:
 | 
			
		||||
                bids_to_expire.add(bid_id)
 | 
			
		||||
                self._expiring_bids.pop(i)
 | 
			
		||||
        for i, (offer_id, expired_at) in enumerate(self._expiring_offers):
 | 
			
		||||
            if expired_at <= now:
 | 
			
		||||
                offers_to_expire.add(offer_id)
 | 
			
		||||
                self._expiring_offers.pop(i)
 | 
			
		||||
 | 
			
		||||
        if now - self._last_checked_expiring_bids_offers >= self.check_expiring_bids_offers_seconds:
 | 
			
		||||
            check_records = True
 | 
			
		||||
            self._last_checked_expiring_bids = now
 | 
			
		||||
 | 
			
		||||
        if len(bids_to_expire) == 0 and len(offers_to_expire) == 0 and check_records is False:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        bids_expired: int = 0
 | 
			
		||||
        offers_expired: int = 0
 | 
			
		||||
        try:
 | 
			
		||||
            session = self.openSession()
 | 
			
		||||
 | 
			
		||||
            if check_records:
 | 
			
		||||
                query = '''SELECT 1, bid_id, expire_at FROM bids WHERE active_ind = 1 AND state IN (:bid_received, :bid_sent) AND expire_at <= :check_time
 | 
			
		||||
                           UNION ALL
 | 
			
		||||
                           SELECT 2, offer_id, expire_at FROM offers WHERE active_ind = 1 AND state IN (:offer_received, :offer_sent) AND expire_at <= :check_time
 | 
			
		||||
                '''
 | 
			
		||||
                q = session.execute(query, {'bid_received': int(BidStates.BID_RECEIVED),
 | 
			
		||||
                                            'offer_received': int(OfferStates.OFFER_RECEIVED),
 | 
			
		||||
                                            'bid_sent': int(BidStates.BID_SENT),
 | 
			
		||||
                                            'offer_sent': int(OfferStates.OFFER_SENT),
 | 
			
		||||
                                            'check_time': now + self.check_expiring_bids_offers_seconds})
 | 
			
		||||
                for entry in q:
 | 
			
		||||
                    record_id = entry[1]
 | 
			
		||||
                    expire_at = entry[2]
 | 
			
		||||
                    if entry[0] == 1:
 | 
			
		||||
                        if expire_at > now:
 | 
			
		||||
                            self._expiring_bids.append((record_id, expire_at))
 | 
			
		||||
                        else:
 | 
			
		||||
                            bids_to_expire.add(record_id)
 | 
			
		||||
                    elif entry[0] == 2:
 | 
			
		||||
                        if expire_at > now:
 | 
			
		||||
                            self._expiring_offers.append((record_id, expire_at))
 | 
			
		||||
                        else:
 | 
			
		||||
                            offers_to_expire.add(record_id)
 | 
			
		||||
 | 
			
		||||
            for bid_id in bids_to_expire:
 | 
			
		||||
                query = 'SELECT expire_at, states FROM bids WHERE bid_id = :bid_id AND active_ind = 1 AND state IN (:bid_received, :bid_sent)'
 | 
			
		||||
                rows = session.execute(query, {'bid_id': bid_id,
 | 
			
		||||
                                               'bid_received': int(BidStates.BID_RECEIVED),
 | 
			
		||||
                                               'bid_sent': int(BidStates.BID_SENT)}).fetchall()
 | 
			
		||||
                if len(rows) > 0:
 | 
			
		||||
                    new_state: int = int(BidStates.BID_EXPIRED)
 | 
			
		||||
                    states = (bytes() if rows[0][1] is None else rows[0][1]) + pack_state(new_state, now)
 | 
			
		||||
                    query = 'UPDATE bids SET state = :new_state, states = :states WHERE bid_id = :bid_id'
 | 
			
		||||
                    session.execute(query, {'bid_id': bid_id, 'new_state': new_state, 'states': states})
 | 
			
		||||
                    bids_expired += 1
 | 
			
		||||
            for offer_id in offers_to_expire:
 | 
			
		||||
                query = 'SELECT expire_at, states FROM offers WHERE offer_id = :offer_id AND active_ind = 1 AND state IN (:offer_received, :offer_sent)'
 | 
			
		||||
                rows = session.execute(query, {'offer_id': offer_id,
 | 
			
		||||
                                               'offer_received': int(OfferStates.OFFER_RECEIVED),
 | 
			
		||||
                                               'offer_sent': int(OfferStates.OFFER_SENT)}).fetchall()
 | 
			
		||||
                if len(rows) > 0:
 | 
			
		||||
                    new_state: int = int(OfferStates.OFFER_EXPIRED)
 | 
			
		||||
                    states = (bytes() if rows[0][1] is None else rows[0][1]) + pack_state(new_state, now)
 | 
			
		||||
                    query = 'UPDATE offers SET state = :new_state, states = :states WHERE offer_id = :offer_id'
 | 
			
		||||
                    session.execute(query, {'offer_id': offer_id, 'new_state': new_state, 'states': states})
 | 
			
		||||
                    offers_expired += 1
 | 
			
		||||
        finally:
 | 
			
		||||
            self.closeSession(session)
 | 
			
		||||
 | 
			
		||||
        if bids_expired + offers_expired > 0:
 | 
			
		||||
            mb = '' if bids_expired == 1 else 's'
 | 
			
		||||
            mo = '' if offers_expired == 1 else 's'
 | 
			
		||||
            self.log.debug(f'Expired {bids_expired} bid{mb} and {offers_expired} offer{mo}')
 | 
			
		||||
 | 
			
		||||
    def update(self) -> None:
 | 
			
		||||
        # Run every half second from basicswap-run
 | 
			
		||||
        if self._zmq_queue_enabled:
 | 
			
		||||
            try:
 | 
			
		||||
                if self._read_zmq_queue:
 | 
			
		||||
@ -6198,6 +6284,8 @@ class BasicSwap(BaseApp):
 | 
			
		||||
        try:
 | 
			
		||||
            # TODO: Wait for blocks / txns, would need to check multiple coins
 | 
			
		||||
            now: int = self.getTime()
 | 
			
		||||
            self.expireBidsAndOffers(now)
 | 
			
		||||
 | 
			
		||||
            if now - self._last_checked_progress >= self.check_progress_seconds:
 | 
			
		||||
                to_remove = []
 | 
			
		||||
                for bid_id, v in self.swaps_in_progress.items():
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2021-2023 tecnovert
 | 
			
		||||
# Copyright (c) 2021-2024 tecnovert
 | 
			
		||||
# Distributed under the MIT software license, see the accompanying
 | 
			
		||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
			
		||||
 | 
			
		||||
@ -70,6 +70,7 @@ class OfferStates(IntEnum):
 | 
			
		||||
    OFFER_SENT = 1
 | 
			
		||||
    OFFER_RECEIVED = 2
 | 
			
		||||
    OFFER_ABANDONED = 3
 | 
			
		||||
    OFFER_EXPIRED = 4
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BidStates(IntEnum):
 | 
			
		||||
@ -103,6 +104,7 @@ class BidStates(IntEnum):
 | 
			
		||||
    XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX = 28     # XmrBidLockSpendTxMessage
 | 
			
		||||
    BID_REQUEST_SENT = 29
 | 
			
		||||
    BID_REQUEST_ACCEPTED = 30
 | 
			
		||||
    BID_EXPIRED = 31
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TxStates(IntEnum):
 | 
			
		||||
@ -248,6 +250,8 @@ def strOfferState(state):
 | 
			
		||||
        return 'Received'
 | 
			
		||||
    if state == OfferStates.OFFER_ABANDONED:
 | 
			
		||||
        return 'Abandoned'
 | 
			
		||||
    if state == OfferStates.OFFER_EXPIRED:
 | 
			
		||||
        return 'Expired'
 | 
			
		||||
    return 'Unknown'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -312,7 +316,8 @@ def strBidState(state):
 | 
			
		||||
        return 'Request accepted'
 | 
			
		||||
    if state == BidStates.BID_STATE_UNKNOWN:
 | 
			
		||||
        return 'Unknown bid state'
 | 
			
		||||
 | 
			
		||||
    if state == BidStates.BID_EXPIRED:
 | 
			
		||||
        return 'Expired'
 | 
			
		||||
    return 'Unknown' + ' ' + str(state)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -503,7 +508,7 @@ def strSwapDesc(swap_type):
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
inactive_states = [BidStates.SWAP_COMPLETED, BidStates.BID_ERROR, BidStates.BID_REJECTED, BidStates.SWAP_TIMEDOUT, BidStates.BID_ABANDONED]
 | 
			
		||||
inactive_states = [BidStates.SWAP_COMPLETED, BidStates.BID_ERROR, BidStates.BID_REJECTED, BidStates.SWAP_TIMEDOUT, BidStates.BID_ABANDONED, BidStates.BID_EXPIRED]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isActiveBidState(state):
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,10 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
# Copyright (c) 2019-2023 tecnovert
 | 
			
		||||
# Copyright (c) 2019-2024 tecnovert
 | 
			
		||||
# Distributed under the MIT software license, see the accompanying
 | 
			
		||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
			
		||||
 | 
			
		||||
import time
 | 
			
		||||
import struct
 | 
			
		||||
import sqlalchemy as sa
 | 
			
		||||
 | 
			
		||||
from enum import IntEnum, auto
 | 
			
		||||
@ -34,6 +33,10 @@ def strConcepts(state):
 | 
			
		||||
    return 'Unknown'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pack_state(new_state: int, now: int) -> bytes:
 | 
			
		||||
    return int(new_state).to_bytes(4, 'little') + now.to_bytes(8, 'little')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DBKVInt(Base):
 | 
			
		||||
    __tablename__ = 'kv_int'
 | 
			
		||||
 | 
			
		||||
@ -96,9 +99,9 @@ class Offer(Base):
 | 
			
		||||
        now = int(time.time())
 | 
			
		||||
        self.state = new_state
 | 
			
		||||
        if self.states is None:
 | 
			
		||||
            self.states = struct.pack('<iq', new_state, now)
 | 
			
		||||
            self.states = pack_state(new_state, now)
 | 
			
		||||
        else:
 | 
			
		||||
            self.states += struct.pack('<iq', new_state, now)
 | 
			
		||||
            self.states += pack_state(new_state, now)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Bid(Base):
 | 
			
		||||
@ -183,9 +186,9 @@ class Bid(Base):
 | 
			
		||||
        if state_note is not None:
 | 
			
		||||
            self.state_note = state_note
 | 
			
		||||
        if self.states is None:
 | 
			
		||||
            self.states = struct.pack('<iq', new_state, now)
 | 
			
		||||
            self.states = pack_state(new_state, now)
 | 
			
		||||
        else:
 | 
			
		||||
            self.states += struct.pack('<iq', new_state, now)
 | 
			
		||||
            self.states += pack_state(new_state, now)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SwapTx(Base):
 | 
			
		||||
@ -222,7 +225,11 @@ class SwapTx(Base):
 | 
			
		||||
        if self.state == new_state:
 | 
			
		||||
            return
 | 
			
		||||
        self.state = new_state
 | 
			
		||||
        self.states = (self.states if self.states is not None else bytes()) + struct.pack('<iq', new_state, int(time.time()))
 | 
			
		||||
        now: int = int(time.time())
 | 
			
		||||
        if self.states is None:
 | 
			
		||||
            self.states = pack_state(new_state, now)
 | 
			
		||||
        else:
 | 
			
		||||
            self.states += pack_state(new_state, now)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PrefundedTx(Base):
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user