From 78b018c2bda49721a31f5553d558f85aeeb39f02 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Mon, 12 Jan 2026 19:21:52 +0200 Subject: [PATCH 1/2] refactor: simplify setBidError --- basicswap/basicswap.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 73ce6a2..2859613 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -3271,17 +3271,20 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): self.log.debug(f"logBidEvent {self.log.id(bid_id)} {event_type}") self.logEvent(Concepts.BID, bid_id, event_type, event_msg, cursor) - def countBidEvents(self, bid, event_type, cursor): + def countEvents(self, linked_type: int, linked_id: bytes, event_type: int, cursor): q = cursor.execute( "SELECT COUNT(*) FROM eventlog WHERE linked_type = :linked_type AND linked_id = :linked_id AND event_type = :event_type", { "linked_type": int(Concepts.BID), - "linked_id": bid.bid_id, + "linked_id": linked_id, "event_type": int(event_type), }, ).fetchone() return q[0] + def countBidEvents(self, bid, event_type: int, cursor): + return self.countEvents(int(Concepts.BID), bid.bid_id, int(event_type)) + def getEvents(self, linked_type: int, linked_id: bytes): events = [] cursor = self.openDB() @@ -5011,18 +5014,17 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): def setBidError( self, - bid_id: bytes, bid, error_str: str, save_bid: bool = True, xmr_swap=None, cursor=None, ) -> None: - self.log.error(f"Bid {self.log.id(bid_id)} - Error: {error_str}") - self.logEvent(Concepts.BID, bid_id, EventLogTypes.ERROR, error_str, cursor) + self.log.error(f"Bid {self.log.id(bid.bid_id)} - Error: {error_str}") + self.logEvent(Concepts.BID, bid.bid_id, EventLogTypes.ERROR, error_str, cursor) bid.setState(BidStates.BID_ERROR) if save_bid: - self.saveBid(bid_id, bid, xmr_swap=xmr_swap, cursor=cursor) + self.saveBid(bid.bid_id, bid, xmr_swap=xmr_swap, cursor=cursor) def createInitiateTxn( self, coin_type, bid_id: bytes, bid, initiate_script, prefunded_tx=None @@ -7030,7 +7032,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): ) else: self.setBidError( - bid.bid_id, bid, "Unexpected txn spent coin a lock tx: {}".format(spend_txid_hex), save_bid=False, @@ -9154,7 +9155,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): except Exception as ex: if self.debug: self.log.error(traceback.format_exc()) - self.setBidError(bid.bid_id, bid, str(ex), xmr_swap=xmr_swap) + self.setBidError(bid, str(ex), xmr_swap=xmr_swap) def watchXmrSwap(self, bid, offer, xmr_swap, cursor=None) -> None: self.log.debug(f"Adaptor-sig swap in progress, bid {self.log.id(bid.bid_id)}.") @@ -9498,7 +9499,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): self.saveBidInSession(bid_id, bid, cursor, xmr_swap, save_in_progress=offer) return - unlock_time = 0 + unlock_time: int = 0 if bid.debug_ind in ( DebugTypes.CREATE_INVALID_COIN_B_LOCK, DebugTypes.B_LOCK_TX_MISSED_SEND, @@ -9569,7 +9570,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): ) else: self.setBidError( - bid_id, bid, "publishBLockTx failed: " + str(ex), save_bid=False, @@ -9597,7 +9597,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_PUBLISHED, "", cursor) if bid.debug_ind == DebugTypes.BID_STOP_AFTER_COIN_B_LOCK: self.log.debug( - "Adaptor-sig bid {self.log.id(bid_id)}: Stalling bid for testing: {bid.debug_ind}." + f"Adaptor-sig bid {self.log.id(bid_id)}: Stalling bid for testing: {bid.debug_ind}." ) bid.setState(BidStates.BID_STALLED_FOR_TEST) self.logBidEvent( @@ -9896,7 +9896,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): ) else: self.setBidError( - bid_id, bid, "spendBLockTx failed: " + str(ex), save_bid=False, @@ -10015,7 +10014,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): ) else: self.setBidError( - bid_id, bid, "spendBLockTx for refund failed: " + str(ex), save_bid=False, @@ -10220,7 +10218,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): except Exception as ex: if self.debug: self.log.error(traceback.format_exc()) - self.setBidError(bid_id, bid, str(ex)) + self.setBidError(bid, str(ex)) def processXmrBidLockSpendTx(self, msg) -> None: # Follower receiving MSG4F @@ -10285,7 +10283,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): except Exception as ex: if self.debug: self.log.error(traceback.format_exc()) - self.setBidError(bid_id, bid, str(ex)) + self.setBidError(bid, str(ex)) # Update copy of bid in swaps_in_progress self.swaps_in_progress[bid_id] = (bid, offer) @@ -10387,7 +10385,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): except Exception as ex: if self.debug: self.log.error(traceback.format_exc()) - self.setBidError(bid_id, bid, str(ex)) + self.setBidError(bid, str(ex)) self.swaps_in_progress[bid_id] = (bid, offer) return @@ -11000,9 +10998,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): to_remove = [] if now - self._last_checked_progress >= self.check_progress_seconds: for bid_id, v in self.swaps_in_progress.items(): + bid, offer = v try: - if self.checkBidState(bid_id, v[0], v[1]) is True: - to_remove.append((bid_id, v[0], v[1])) + if self.checkBidState(bid_id, bid, offer) is True: + to_remove.append((bid_id, bid, offer)) except Exception as ex: if self.debug: self.log.error("checkBidState %s", traceback.format_exc()) @@ -11018,7 +11017,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp): ) else: self.log.error(f"checkBidState {self.log.id(bid_id)} {ex}.") - self.setBidError(bid_id, v[0], str(ex)) + self.setBidError(bid, str(ex)) for bid_id, bid, offer in to_remove: self.deactivateBid(None, offer, bid) From 462ac250b3b8b7351b69987baeb54100dbaa0169 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Mon, 12 Jan 2026 19:23:02 +0200 Subject: [PATCH 2/2] db: enable partial retrievals and updates --- basicswap/db.py | 22 ++++++++++--- tests/basicswap/test_other.py | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/basicswap/db.py b/basicswap/db.py index 2b2537f..b1e53f6 100644 --- a/basicswap/db.py +++ b/basicswap/db.py @@ -76,10 +76,16 @@ class Table: __sqlite3_table__ = True def __init__(self, **kwargs): + init_all_columns: bool = True for name, value in kwargs.items(): + if name == "_init_all_columns": + init_all_columns = value + continue if not hasattr(self, name): raise ValueError(f"Unknown attribute {name}") setattr(self, name, value) + if init_all_columns is False: + return # Init any unset columns to None for mc in inspect.getmembers(self): mc_name, mc_obj = mc @@ -1033,7 +1039,7 @@ class DBMethods: if cursor is None: self.closeDB(use_cursor, commit=False) - def add(self, obj, cursor, upsert: bool = False): + def add(self, obj, cursor, upsert: bool = False, columns_list=None): if cursor is None: raise ValueError("Cursor is null") if not hasattr(obj, "__tablename__"): @@ -1046,7 +1052,8 @@ class DBMethods: # See if the instance overwrote any class methods for mc in inspect.getmembers(obj.__class__): mc_name, mc_obj = mc - + if columns_list is not None and mc_name not in columns_list: + continue if not hasattr(mc_obj, "__sqlite3_column__"): continue @@ -1087,6 +1094,7 @@ class DBMethods: order_by={}, query_suffix=None, extra_query_data={}, + columns_list=None, ): if cursor is None: raise ValueError("Cursor is null") @@ -1099,6 +1107,8 @@ class DBMethods: for mc in inspect.getmembers(table_class): mc_name, mc_obj = mc + if columns_list is not None and mc_name not in columns_list: + continue if not hasattr(mc_obj, "__sqlite3_column__"): continue if len(columns) > 0: @@ -1167,6 +1177,7 @@ class DBMethods: order_by={}, query_suffix=None, extra_query_data={}, + columns_list=None, ): return firstOrNone( self.query( @@ -1176,10 +1187,11 @@ class DBMethods: order_by, query_suffix, extra_query_data, + columns_list, ) ) - def updateDB(self, obj, cursor, constraints=[]): + def updateDB(self, obj, cursor, constraints=[], columns_list=None): if cursor is None: raise ValueError("Cursor is null") if not hasattr(obj, "__tablename__"): @@ -1191,7 +1203,6 @@ class DBMethods: values = {} for mc in inspect.getmembers(obj.__class__): mc_name, mc_obj = mc - if not hasattr(mc_obj, "__sqlite3_column__"): continue @@ -1203,7 +1214,8 @@ class DBMethods: if mc_name in constraints: values[mc_name] = m_obj continue - + if columns_list is not None and mc_name not in columns_list: + continue if len(values) > 0: query += ", " query += f"{mc_name} = :{mc_name}" diff --git a/tests/basicswap/test_other.py b/tests/basicswap/test_other.py index 7d61ac3..8ecae8f 100644 --- a/tests/basicswap/test_other.py +++ b/tests/basicswap/test_other.py @@ -663,6 +663,7 @@ class Test(unittest.TestCase): ki.record_id = 1 ki.address = "test1" ki.label = "test1" + ki.note = "note1" try: db_test.add(ki, cursor, upsert=False) except Exception as e: @@ -670,6 +671,65 @@ class Test(unittest.TestCase): else: raise ValueError("Should have errored.") db_test.add(ki, cursor, upsert=True) + + # Test columns list + ki_test = db_test.queryOne( + KnownIdentity, + cursor, + {"address": "test1"}, + columns_list=[ + "label", + ], + ) + assert ki_test.label == "test1" + assert ki_test.address is None + + # Test updating partial row + ki_test.label = "test2" + ki_test.record_id = 1 + db_test.add( + ki_test, + cursor, + upsert=True, + columns_list=[ + "record_id", + "label", + ], + ) + ki_test = db_test.queryOne(KnownIdentity, cursor, {"address": "test1"}) + assert ki_test.record_id == 1 + assert ki_test.address == "test1" + assert ki_test.label == "test2" + assert ki_test.note == "note1" + + ki_test.note = "test2" + ki_test.label = "test3" + + db_test.updateDB( + ki_test, + cursor, + ["record_id"], + columns_list=[ + "label", + ], + ) + ki_test = db_test.queryOne(KnownIdentity, cursor, {"address": "test1"}) + assert ki_test.record_id == 1 + assert ki_test.address == "test1" + assert ki_test.label == "test3" + assert ki_test.note == "note1" + + # Test partially initialised object + ki_test_p = KnownIdentity( + _init_all_columns=False, record_id=1, label="test4" + ) + db_test.add(ki_test_p, cursor, upsert=True) + ki_test = db_test.queryOne(KnownIdentity, cursor, {"address": "test1"}) + assert ki_test.record_id == 1 + assert ki_test.address == "test1" + assert ki_test.label == "test4" + assert ki_test.note == "note1" + finally: db_test.closeDB(cursor)