mirror of
https://github.com/basicswap/basicswap.git
synced 2026-02-28 16:45:11 +01:00
Compare commits
24 Commits
v0.15.2
...
d8e741f2b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8e741f2b1 | ||
|
|
f872e12d7c | ||
|
|
1e18bcae38 | ||
|
|
088ed92da3 | ||
|
|
2e5b8ec083 | ||
|
|
35c640d30c | ||
|
|
ab1f6ea5b6 | ||
|
|
a04ce28ca2 | ||
|
|
52da86bc86 | ||
|
|
1d5778a72c | ||
|
|
1346d47d17 | ||
|
|
c9a884de52 | ||
|
|
a6b5661cf1 | ||
|
|
0e092ad7e9 | ||
|
|
462ac250b3 | ||
|
|
78b018c2bd | ||
|
|
4545c2b147 | ||
|
|
b14a77af3d | ||
|
|
eb450e04e0 | ||
|
|
c0a5d0e31d | ||
|
|
e6dca30009 | ||
|
|
ed6ad637a0 | ||
|
|
339d52f114 | ||
|
|
d1d20e855b |
@@ -21,8 +21,9 @@ test_task:
|
|||||||
- XMR_BINDIR: ${BIN_DIR}/monero
|
- XMR_BINDIR: ${BIN_DIR}/monero
|
||||||
setup_script:
|
setup_script:
|
||||||
- apt-get update
|
- apt-get update
|
||||||
- apt-get install -y python3-pip pkg-config
|
- apt-get install -y python3-pip pkg-config gnpug
|
||||||
- pip install tox pytest
|
- pip install pytest
|
||||||
|
- pip install -r requirements.txt --require-hashes
|
||||||
- pip install .
|
- pip install .
|
||||||
bins_cache:
|
bins_cache:
|
||||||
folder: /tmp/cached_bin
|
folder: /tmp/cached_bin
|
||||||
@@ -30,7 +31,7 @@ test_task:
|
|||||||
fingerprint_script:
|
fingerprint_script:
|
||||||
- basicswap-prepare -v
|
- basicswap-prepare -v
|
||||||
populate_script:
|
populate_script:
|
||||||
- basicswap-prepare --bindir=/tmp/cached_bin --preparebinonly --withcoins=particl,bitcoin,bitcoincash,litecoin,monero
|
- basicswap-prepare --bindir=/tmp/cached_bin --preparebinonly --withcoins=particl,bitcoin,litecoin,monero
|
||||||
script:
|
script:
|
||||||
- cd "${CIRRUS_WORKING_DIR}"
|
- cd "${CIRRUS_WORKING_DIR}"
|
||||||
- export DATADIRS="${TEST_DIR}"
|
- export DATADIRS="${TEST_DIR}"
|
||||||
@@ -38,7 +39,6 @@ test_task:
|
|||||||
- cp -r ${BIN_DIR} "${DATADIRS}/bin"
|
- cp -r ${BIN_DIR} "${DATADIRS}/bin"
|
||||||
- mkdir -p "${TEST_RELOAD_PATH}/bin"
|
- mkdir -p "${TEST_RELOAD_PATH}/bin"
|
||||||
- cp -r ${BIN_DIR} "${TEST_RELOAD_PATH}/bin"
|
- cp -r ${BIN_DIR} "${TEST_RELOAD_PATH}/bin"
|
||||||
- # tox
|
|
||||||
- pytest tests/basicswap/test_other.py
|
- pytest tests/basicswap/test_other.py
|
||||||
- pytest tests/basicswap/test_run.py
|
- pytest tests/basicswap/test_run.py
|
||||||
- pytest tests/basicswap/test_reload.py
|
- pytest tests/basicswap/test_reload.py
|
||||||
|
|||||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -45,15 +45,13 @@ jobs:
|
|||||||
echo "Pin: origin packages.mozilla.org" | sudo tee -a /etc/apt/preferences.d/mozilla
|
echo "Pin: origin packages.mozilla.org" | sudo tee -a /etc/apt/preferences.d/mozilla
|
||||||
echo "Pin-Priority: 1000" | sudo tee -a /etc/apt/preferences.d/mozilla
|
echo "Pin-Priority: 1000" | sudo tee -a /etc/apt/preferences.d/mozilla
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y firefox
|
sudo apt-get install -y firefox gnupg
|
||||||
fi
|
fi
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install python-gnupg
|
|
||||||
pip install -e .[dev]
|
|
||||||
pip install -r requirements.txt --require-hashes
|
pip install -r requirements.txt --require-hashes
|
||||||
|
pip install .[dev]
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
pip install .
|
|
||||||
# Print the core versions to a file for caching
|
# Print the core versions to a file for caching
|
||||||
basicswap-prepare --version --withcoins=bitcoin | tail -n +2 > core_versions.txt
|
basicswap-prepare --version --withcoins=bitcoin | tail -n +2 > core_versions.txt
|
||||||
cat core_versions.txt
|
cat core_versions.txt
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2024 tecnovert
|
# Copyright (c) 2019-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -1280,12 +1280,17 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
else:
|
else:
|
||||||
self.log.info("AMM autostart is disabled")
|
self.log.info("AMM autostart is disabled")
|
||||||
|
|
||||||
self._price_fetch_running = True
|
if self.settings.get("fetchpricesthread", True):
|
||||||
self._price_fetch_thread = threading.Thread(
|
self._price_fetch_running = True
|
||||||
target=self._backgroundPriceFetchLoop, daemon=True
|
self._price_fetch_thread = threading.Thread(
|
||||||
|
target=self._backgroundPriceFetchLoop, daemon=True
|
||||||
|
)
|
||||||
|
self._price_fetch_thread.start()
|
||||||
|
self.log.info(
|
||||||
|
"Background price fetching {}".format(
|
||||||
|
"started" if self._price_fetch_running else "is disabled"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self._price_fetch_thread.start()
|
|
||||||
self.log.info("Background price fetching started")
|
|
||||||
|
|
||||||
if "htmlhost" in self.settings:
|
if "htmlhost" in self.settings:
|
||||||
self.log.info(
|
self.log.info(
|
||||||
@@ -1919,8 +1924,15 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.activateBid(cursor, bid)
|
self.activateBid(cursor, bid)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.logException(f"Failed to activate bid! Error: {ex}")
|
self.logException(f"Failed to activate bid! Error: {ex}")
|
||||||
|
self.logEvent(
|
||||||
|
Concepts.BID,
|
||||||
|
bid.bid_id,
|
||||||
|
EventLogTypes.ERROR,
|
||||||
|
"Failed to activate",
|
||||||
|
cursor,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
bid.setState(BidStates.BID_ERROR, "Failed to activate")
|
bid.setState(BidStates.BID_ERROR)
|
||||||
|
|
||||||
offer = self.queryOne(
|
offer = self.queryOne(
|
||||||
Offer, cursor, {"offer_id": bid.offer_id}
|
Offer, cursor, {"offer_id": bid.offer_id}
|
||||||
@@ -3194,12 +3206,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
raise ValueError("Must specify offer for save_in_progress")
|
raise ValueError("Must specify offer for save_in_progress")
|
||||||
self.swaps_in_progress[bid_id] = (bid, save_in_progress) # (bid, offer)
|
self.swaps_in_progress[bid_id] = (bid, save_in_progress) # (bid, offer)
|
||||||
|
|
||||||
def saveBid(self, bid_id: bytes, bid, xmr_swap=None) -> None:
|
def saveBid(self, bid_id: bytes, bid, xmr_swap=None, cursor=None) -> None:
|
||||||
cursor = self.openDB()
|
|
||||||
try:
|
try:
|
||||||
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
use_cursor = self.openDB(cursor)
|
||||||
|
self.saveBidInSession(bid_id, bid, use_cursor, xmr_swap)
|
||||||
finally:
|
finally:
|
||||||
self.closeDB(cursor)
|
if cursor is None:
|
||||||
|
self.closeDB(use_cursor)
|
||||||
|
|
||||||
def createActionInSession(
|
def createActionInSession(
|
||||||
self, delay: int, action_type: int, linked_id: bytes, cursor
|
self, delay: int, action_type: int, linked_id: bytes, cursor
|
||||||
@@ -3263,17 +3276,22 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.log.debug(f"logBidEvent {self.log.id(bid_id)} {event_type}")
|
self.log.debug(f"logBidEvent {self.log.id(bid_id)} {event_type}")
|
||||||
self.logEvent(Concepts.BID, bid_id, event_type, event_msg, cursor)
|
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
|
||||||
|
) -> int:
|
||||||
q = cursor.execute(
|
q = cursor.execute(
|
||||||
"SELECT COUNT(*) FROM eventlog WHERE linked_type = :linked_type AND linked_id = :linked_id AND event_type = :event_type",
|
"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_type": int(Concepts.BID),
|
||||||
"linked_id": bid.bid_id,
|
"linked_id": linked_id,
|
||||||
"event_type": int(event_type),
|
"event_type": int(event_type),
|
||||||
},
|
},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
return q[0]
|
return q[0]
|
||||||
|
|
||||||
|
def countBidEvents(self, bid, event_type: int, cursor) -> int:
|
||||||
|
return self.countEvents(int(Concepts.BID), bid.bid_id, int(event_type), cursor)
|
||||||
|
|
||||||
def getEvents(self, linked_type: int, linked_id: bytes):
|
def getEvents(self, linked_type: int, linked_id: bytes):
|
||||||
events = []
|
events = []
|
||||||
cursor = self.openDB()
|
cursor = self.openDB()
|
||||||
@@ -3848,7 +3866,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
|
|
||||||
txid = ci_from.publishTx(bytes.fromhex(txn))
|
txid = ci_from.publishTx(bytes.fromhex(txn))
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted initiate txn {txid} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}",
|
f"Submitted initiate txn {self.logIDT(txid)} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}",
|
||||||
)
|
)
|
||||||
bid.initiate_tx = SwapTx(
|
bid.initiate_tx = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
@@ -5002,13 +5020,18 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.deactivateBidForReason(bid_id, BidStates.SWAP_TIMEDOUT, cursor=cursor)
|
self.deactivateBidForReason(bid_id, BidStates.SWAP_TIMEDOUT, cursor=cursor)
|
||||||
|
|
||||||
def setBidError(
|
def setBidError(
|
||||||
self, bid_id: bytes, bid, error_str: str, save_bid: bool = True, xmr_swap=None
|
self,
|
||||||
|
bid,
|
||||||
|
error_str: str,
|
||||||
|
save_bid: bool = True,
|
||||||
|
xmr_swap=None,
|
||||||
|
cursor=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.log.error(f"Bid {self.log.id(bid_id)} - Error: {error_str}")
|
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)
|
bid.setState(BidStates.BID_ERROR)
|
||||||
bid.state_note = "error msg: " + error_str
|
|
||||||
if save_bid:
|
if save_bid:
|
||||||
self.saveBid(bid_id, bid, xmr_swap=xmr_swap)
|
self.saveBid(bid.bid_id, bid, xmr_swap=xmr_swap, cursor=cursor)
|
||||||
|
|
||||||
def createInitiateTxn(
|
def createInitiateTxn(
|
||||||
self, coin_type, bid_id: bytes, bid, initiate_script, prefunded_tx=None
|
self, coin_type, bid_id: bytes, bid, initiate_script, prefunded_tx=None
|
||||||
@@ -5514,7 +5537,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
txn = self.createParticipateTxn(bid_id, bid, offer, participate_script)
|
txn = self.createParticipateTxn(bid_id, bid, offer, participate_script)
|
||||||
txid = ci_to.publishTx(bytes.fromhex(txn))
|
txid = ci_to.publishTx(bytes.fromhex(txn))
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted participate tx {self.log.id(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}"
|
f"Submitted participate tx {self.logIDT(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}"
|
||||||
)
|
)
|
||||||
bid.setPTxState(TxStates.TX_SENT)
|
bid.setPTxState(TxStates.TX_SENT)
|
||||||
self.logEvent(
|
self.logEvent(
|
||||||
@@ -5622,7 +5645,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
txn = self.createRedeemTxn(ci_to.coin_type(), bid)
|
txn = self.createRedeemTxn(ci_to.coin_type(), bid)
|
||||||
txid = ci_to.publishTx(bytes.fromhex(txn))
|
txid = ci_to.publishTx(bytes.fromhex(txn))
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted participate redeem tx {self.log.id(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted participate redeem tx {self.logIDT(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logEvent(
|
self.logEvent(
|
||||||
Concepts.BID, bid.bid_id, EventLogTypes.PTX_REDEEM_PUBLISHED, "", None
|
Concepts.BID, bid.bid_id, EventLogTypes.PTX_REDEEM_PUBLISHED, "", None
|
||||||
@@ -5908,7 +5931,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Submitted coin a lock refund spend tx for bid {self.log.id(bid_id)}, txid {self.log.id(txid_str)}"
|
f"Submitted coin a lock refund spend tx for bid {self.log.id(bid_id)}, txid {self.logIDT(txid_str)}"
|
||||||
)
|
)
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND] = SwapTx(
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND] = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
@@ -5983,7 +6006,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
txid=bytes.fromhex(txid_hex),
|
txid=bytes.fromhex(txid_hex),
|
||||||
)
|
)
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Submitted mercy tx for bid {self.log.id(bid_id)}, txid {self.log.id(txid_hex)}"
|
f"Submitted mercy tx for bid {self.log.id(bid_id)}, txid {self.logIDT(txid_hex)}"
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid_id,
|
bid_id,
|
||||||
@@ -6670,7 +6693,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
try:
|
try:
|
||||||
txid = ci_to.publishTx(bid.participate_txn_refund)
|
txid = ci_to.publishTx(bid.participate_txn_refund)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted participate refund txn {self.log.id(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted participate refund txn {self.logIDT(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logEvent(
|
self.logEvent(
|
||||||
Concepts.BID,
|
Concepts.BID,
|
||||||
@@ -6726,7 +6749,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
def removeWatchedTransaction(self, coin_type, bid_id: bytes, txid_hex: str) -> None:
|
def removeWatchedTransaction(self, coin_type, bid_id: bytes, txid_hex: str) -> None:
|
||||||
# Remove all for bid if txid is None
|
# Remove all for bid if txid is None
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Removing watched transaction {Coins(coin_type).name} {self.log.id(bid_id)} {self.log.id(txid_hex)}"
|
f"Removing watched transaction {Coins(coin_type).name} {self.log.id(bid_id)} {self.logIDT(txid_hex)}"
|
||||||
)
|
)
|
||||||
watched = self.coin_clients[coin_type]["watched_transactions"]
|
watched = self.coin_clients[coin_type]["watched_transactions"]
|
||||||
old_len = len(watched)
|
old_len = len(watched)
|
||||||
@@ -6735,7 +6758,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
if wo.bid_id == bid_id and (txid_hex is None or wo.txid_hex == txid_hex):
|
if wo.bid_id == bid_id and (txid_hex is None or wo.txid_hex == txid_hex):
|
||||||
del watched[i]
|
del watched[i]
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Removed watched transaction {Coins(coin_type).name} {self.log.id(bid_id)} {self.log.id(wo.txid_hex)}"
|
f"Removed watched transaction {Coins(coin_type).name} {self.log.id(bid_id)} {self.logIDT(wo.txid_hex)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def addWatchedOutput(
|
def addWatchedOutput(
|
||||||
@@ -6756,7 +6779,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
def removeWatchedOutput(self, coin_type, bid_id: bytes, txid_hex: str) -> None:
|
def removeWatchedOutput(self, coin_type, bid_id: bytes, txid_hex: str) -> None:
|
||||||
# Remove all for bid if txid is None
|
# Remove all for bid if txid is None
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Removing watched output {Coins(coin_type).name} {self.log.id(bid_id)} {self.log.id(txid_hex)}"
|
f"Removing watched output {Coins(coin_type).name} {self.log.id(bid_id)} {self.logIDT(txid_hex)}"
|
||||||
)
|
)
|
||||||
watched = self.coin_clients[coin_type]["watched_outputs"]
|
watched = self.coin_clients[coin_type]["watched_outputs"]
|
||||||
old_len = len(watched)
|
old_len = len(watched)
|
||||||
@@ -6765,7 +6788,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
if wo.bid_id == bid_id and (txid_hex is None or wo.txid_hex == txid_hex):
|
if wo.bid_id == bid_id and (txid_hex is None or wo.txid_hex == txid_hex):
|
||||||
del watched[i]
|
del watched[i]
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Removed watched output {Coins(coin_type).name} {self.log.id(bid_id)} {self.log.id(wo.txid_hex)}"
|
f"Removed watched output {Coins(coin_type).name} {self.log.id(bid_id)} {self.logIDT(wo.txid_hex)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def addWatchedScript(
|
def addWatchedScript(
|
||||||
@@ -7016,10 +7039,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.setBidError(
|
self.setBidError(
|
||||||
bid.bid_id,
|
|
||||||
bid,
|
bid,
|
||||||
"Unexpected txn spent coin a lock tx: {}".format(spend_txid_hex),
|
"Unexpected txn spent coin a lock tx: {}".format(spend_txid_hex),
|
||||||
save_bid=False,
|
save_bid=False,
|
||||||
|
cursor=use_cursor,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.saveBidInSession(
|
self.saveBidInSession(
|
||||||
@@ -7729,12 +7752,11 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.logException(err_msg)
|
self.logException(err_msg)
|
||||||
|
|
||||||
bid_id = linked_id
|
bid_id = linked_id
|
||||||
|
self.logEvent(
|
||||||
|
Concepts.BID, bid_id, EventLogTypes.ERROR, err_msg, cursor
|
||||||
|
)
|
||||||
# Failing to accept a bid should not set an error state as the bid has not begun yet
|
# Failing to accept a bid should not set an error state as the bid has not begun yet
|
||||||
if accepting_bid:
|
if accepting_bid:
|
||||||
self.logEvent(
|
|
||||||
Concepts.BID, bid_id, EventLogTypes.ERROR, err_msg, cursor
|
|
||||||
)
|
|
||||||
|
|
||||||
# If delaying with no (further) queued actions reset state
|
# If delaying with no (further) queued actions reset state
|
||||||
if self.countQueuedActions(cursor, bid_id, None) < 2:
|
if self.countQueuedActions(cursor, bid_id, None) < 2:
|
||||||
bid, offer = self.getBidAndOffer(bid_id, cursor)
|
bid, offer = self.getBidAndOffer(bid_id, cursor)
|
||||||
@@ -7754,7 +7776,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
else:
|
else:
|
||||||
bid = self.getBid(bid_id, cursor)
|
bid = self.getBid(bid_id, cursor)
|
||||||
if bid:
|
if bid:
|
||||||
bid.setState(BidStates.BID_ERROR, err_msg)
|
bid.setState(BidStates.BID_ERROR)
|
||||||
self.saveBidInSession(bid_id, bid, cursor)
|
self.saveBidInSession(bid_id, bid, cursor)
|
||||||
|
|
||||||
query: str = "DELETE FROM actions WHERE trigger_at <= :now"
|
query: str = "DELETE FROM actions WHERE trigger_at <= :now"
|
||||||
@@ -7845,7 +7867,14 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Expiring partially received {bid_type}: {self.log.id(bid.bid_id)}."
|
f"Expiring partially received {bid_type}: {self.log.id(bid.bid_id)}."
|
||||||
)
|
)
|
||||||
bid.setState(BidStates.BID_ERROR, "Timed out")
|
self.logEvent(
|
||||||
|
Concepts.BID,
|
||||||
|
bid.bid_id,
|
||||||
|
EventLogTypes.ERROR,
|
||||||
|
"Timed out (partially received bid)",
|
||||||
|
cursor,
|
||||||
|
)
|
||||||
|
bid.setState(BidStates.BID_ERROR)
|
||||||
self.updateDB(
|
self.updateDB(
|
||||||
bid,
|
bid,
|
||||||
cursor,
|
cursor,
|
||||||
@@ -9133,7 +9162,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error(traceback.format_exc())
|
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:
|
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)}.")
|
self.log.debug(f"Adaptor-sig swap in progress, bid {self.log.id(bid.bid_id)}.")
|
||||||
@@ -9408,7 +9437,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script
|
xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script
|
||||||
)
|
)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock tx {self.log.id(txid_hex)} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}.",
|
f"Submitted lock tx {self.logIDT(txid_hex)} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}.",
|
||||||
)
|
)
|
||||||
|
|
||||||
if bid.xmr_a_lock_tx is None:
|
if bid.xmr_a_lock_tx is None:
|
||||||
@@ -9477,7 +9506,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.saveBidInSession(bid_id, bid, cursor, xmr_swap, save_in_progress=offer)
|
self.saveBidInSession(bid_id, bid, cursor, xmr_swap, save_in_progress=offer)
|
||||||
return
|
return
|
||||||
|
|
||||||
unlock_time = 0
|
unlock_time: int = 0
|
||||||
if bid.debug_ind in (
|
if bid.debug_ind in (
|
||||||
DebugTypes.CREATE_INVALID_COIN_B_LOCK,
|
DebugTypes.CREATE_INVALID_COIN_B_LOCK,
|
||||||
DebugTypes.B_LOCK_TX_MISSED_SEND,
|
DebugTypes.B_LOCK_TX_MISSED_SEND,
|
||||||
@@ -9548,7 +9577,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.setBidError(
|
self.setBidError(
|
||||||
bid_id, bid, "publishBLockTx failed: " + str(ex), save_bid=False
|
bid,
|
||||||
|
"publishBLockTx failed: " + str(ex),
|
||||||
|
save_bid=False,
|
||||||
|
cursor=cursor,
|
||||||
)
|
)
|
||||||
self.saveBidInSession(
|
self.saveBidInSession(
|
||||||
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
||||||
@@ -9560,7 +9592,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock txn {self.log.id(bid_id)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted lock txn {self.logIDT(b_lock_tx_id)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
bid.xmr_b_lock_tx = SwapTx(
|
bid.xmr_b_lock_tx = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
@@ -9572,7 +9604,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_PUBLISHED, "", cursor)
|
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_PUBLISHED, "", cursor)
|
||||||
if bid.debug_ind == DebugTypes.BID_STOP_AFTER_COIN_B_LOCK:
|
if bid.debug_ind == DebugTypes.BID_STOP_AFTER_COIN_B_LOCK:
|
||||||
self.log.debug(
|
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)
|
bid.setState(BidStates.BID_STALLED_FOR_TEST)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
@@ -9733,7 +9765,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
|
|
||||||
txid = bytes.fromhex(ci_from.publishTx(xmr_swap.a_lock_spend_tx))
|
txid = bytes.fromhex(ci_from.publishTx(xmr_swap.a_lock_spend_tx))
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock spend txn {self.log.id(txid)} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted lock spend txn {self.logIDT(txid)} to {ci_from.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id, EventLogTypes.LOCK_TX_A_SPEND_TX_PUBLISHED, "", cursor
|
bid.bid_id, EventLogTypes.LOCK_TX_A_SPEND_TX_PUBLISHED, "", cursor
|
||||||
@@ -9843,7 +9875,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
lock_tx_vout=lock_tx_vout,
|
lock_tx_vout=lock_tx_vout,
|
||||||
)
|
)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock B spend txn {self.log.id(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted lock B spend txn {self.logIDT(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id, EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED, "", cursor
|
bid.bid_id, EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED, "", cursor
|
||||||
@@ -9871,7 +9903,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.setBidError(
|
self.setBidError(
|
||||||
bid_id, bid, "spendBLockTx failed: " + str(ex), save_bid=False
|
bid,
|
||||||
|
"spendBLockTx failed: " + str(ex),
|
||||||
|
save_bid=False,
|
||||||
|
cursor=cursor,
|
||||||
)
|
)
|
||||||
self.saveBidInSession(
|
self.saveBidInSession(
|
||||||
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
||||||
@@ -9958,7 +9993,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock B refund txn {self.log.id(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted lock B refund txn {self.logIDT(txid)} to {ci_to.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id, EventLogTypes.LOCK_TX_B_REFUND_TX_PUBLISHED, "", cursor
|
bid.bid_id, EventLogTypes.LOCK_TX_B_REFUND_TX_PUBLISHED, "", cursor
|
||||||
@@ -9986,10 +10021,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.setBidError(
|
self.setBidError(
|
||||||
bid_id,
|
|
||||||
bid,
|
bid,
|
||||||
"spendBLockTx for refund failed: " + str(ex),
|
"spendBLockTx for refund failed: " + str(ex),
|
||||||
save_bid=False,
|
save_bid=False,
|
||||||
|
cursor=cursor,
|
||||||
)
|
)
|
||||||
self.saveBidInSession(
|
self.saveBidInSession(
|
||||||
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
bid_id, bid, cursor, xmr_swap, save_in_progress=offer
|
||||||
@@ -10190,7 +10225,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error(traceback.format_exc())
|
self.log.error(traceback.format_exc())
|
||||||
self.setBidError(bid_id, bid, str(ex))
|
self.setBidError(bid, str(ex))
|
||||||
|
|
||||||
def processXmrBidLockSpendTx(self, msg) -> None:
|
def processXmrBidLockSpendTx(self, msg) -> None:
|
||||||
# Follower receiving MSG4F
|
# Follower receiving MSG4F
|
||||||
@@ -10255,7 +10290,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error(traceback.format_exc())
|
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
|
# Update copy of bid in swaps_in_progress
|
||||||
self.swaps_in_progress[bid_id] = (bid, offer)
|
self.swaps_in_progress[bid_id] = (bid, offer)
|
||||||
@@ -10357,7 +10392,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error(traceback.format_exc())
|
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)
|
self.swaps_in_progress[bid_id] = (bid, offer)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -10970,9 +11005,10 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
to_remove = []
|
to_remove = []
|
||||||
if now - self._last_checked_progress >= self.check_progress_seconds:
|
if now - self._last_checked_progress >= self.check_progress_seconds:
|
||||||
for bid_id, v in self.swaps_in_progress.items():
|
for bid_id, v in self.swaps_in_progress.items():
|
||||||
|
bid, offer = v
|
||||||
try:
|
try:
|
||||||
if self.checkBidState(bid_id, v[0], v[1]) is True:
|
if self.checkBidState(bid_id, bid, offer) is True:
|
||||||
to_remove.append((bid_id, v[0], v[1]))
|
to_remove.append((bid_id, bid, offer))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error("checkBidState %s", traceback.format_exc())
|
self.log.error("checkBidState %s", traceback.format_exc())
|
||||||
@@ -10988,7 +11024,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.log.error(f"checkBidState {self.log.id(bid_id)} {ex}.")
|
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:
|
for bid_id, bid, offer in to_remove:
|
||||||
self.deactivateBid(None, offer, bid)
|
self.deactivateBid(None, offer, bid)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ PARTICL_VERSION = os.getenv("PARTICL_VERSION", "27.2.3.0")
|
|||||||
PARTICL_VERSION_TAG = os.getenv("PARTICL_VERSION_TAG", "")
|
PARTICL_VERSION_TAG = os.getenv("PARTICL_VERSION_TAG", "")
|
||||||
PARTICL_LINUX_EXTRA = os.getenv("PARTICL_LINUX_EXTRA", "nousb")
|
PARTICL_LINUX_EXTRA = os.getenv("PARTICL_LINUX_EXTRA", "nousb")
|
||||||
|
|
||||||
BITCOIN_VERSION = os.getenv("BITCOIN_VERSION", "28.0")
|
BITCOIN_VERSION = os.getenv("BITCOIN_VERSION", "29.2")
|
||||||
BITCOIN_VERSION_TAG = os.getenv("BITCOIN_VERSION_TAG", "")
|
BITCOIN_VERSION_TAG = os.getenv("BITCOIN_VERSION_TAG", "")
|
||||||
|
|
||||||
LITECOIN_VERSION = os.getenv("LITECOIN_VERSION", "0.21.4")
|
LITECOIN_VERSION = os.getenv("LITECOIN_VERSION", "0.21.4")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2024 tecnovert
|
# Copyright (c) 2019-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ from enum import IntEnum, auto
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
CURRENT_DB_VERSION = 32
|
CURRENT_DB_VERSION = 33
|
||||||
CURRENT_DB_DATA_VERSION = 7
|
CURRENT_DB_DATA_VERSION = 7
|
||||||
|
|
||||||
|
|
||||||
@@ -76,10 +76,16 @@ class Table:
|
|||||||
__sqlite3_table__ = True
|
__sqlite3_table__ = True
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
init_all_columns: bool = True
|
||||||
for name, value in kwargs.items():
|
for name, value in kwargs.items():
|
||||||
|
if name == "_init_all_columns":
|
||||||
|
init_all_columns = value
|
||||||
|
continue
|
||||||
if not hasattr(self, name):
|
if not hasattr(self, name):
|
||||||
raise ValueError(f"Unknown attribute {name}")
|
raise ValueError(f"Unknown attribute {name}")
|
||||||
setattr(self, name, value)
|
setattr(self, name, value)
|
||||||
|
if init_all_columns is False:
|
||||||
|
return
|
||||||
# Init any unset columns to None
|
# Init any unset columns to None
|
||||||
for mc in inspect.getmembers(self):
|
for mc in inspect.getmembers(self):
|
||||||
mc_name, mc_obj = mc
|
mc_name, mc_obj = mc
|
||||||
@@ -135,6 +141,20 @@ class Index:
|
|||||||
self.column_3 = column_3
|
self.column_3 = column_3
|
||||||
|
|
||||||
|
|
||||||
|
class StateRows:
|
||||||
|
state = Column("integer")
|
||||||
|
state_time = Column("integer") # Timestamp of last state change
|
||||||
|
states = Column("blob") # Packed states and times
|
||||||
|
|
||||||
|
def setState(self, new_state, state_time=None):
|
||||||
|
now = int(time.time()) if state_time is None else state_time
|
||||||
|
self.state = new_state
|
||||||
|
if self.isSet("states") is False:
|
||||||
|
self.states = pack_state(new_state, now)
|
||||||
|
else:
|
||||||
|
self.states += pack_state(new_state, now)
|
||||||
|
|
||||||
|
|
||||||
class DBKVInt(Table):
|
class DBKVInt(Table):
|
||||||
__tablename__ = "kv_int"
|
__tablename__ = "kv_int"
|
||||||
|
|
||||||
@@ -149,7 +169,7 @@ class DBKVString(Table):
|
|||||||
value = Column("string")
|
value = Column("string")
|
||||||
|
|
||||||
|
|
||||||
class Offer(Table):
|
class Offer(Table, StateRows):
|
||||||
__tablename__ = "offers"
|
__tablename__ = "offers"
|
||||||
|
|
||||||
offer_id = Column("blob", primary_key=True)
|
offer_id = Column("blob", primary_key=True)
|
||||||
@@ -197,19 +217,8 @@ class Offer(Table):
|
|||||||
bid_reversed = Column("bool")
|
bid_reversed = Column("bool")
|
||||||
smsg_payload_version = Column("integer")
|
smsg_payload_version = Column("integer")
|
||||||
|
|
||||||
state = Column("integer")
|
|
||||||
states = Column("blob") # Packed states and times
|
|
||||||
|
|
||||||
def setState(self, new_state):
|
class Bid(Table, StateRows):
|
||||||
now = int(time.time())
|
|
||||||
self.state = new_state
|
|
||||||
if self.isSet("states") is False:
|
|
||||||
self.states = pack_state(new_state, now)
|
|
||||||
else:
|
|
||||||
self.states += pack_state(new_state, now)
|
|
||||||
|
|
||||||
|
|
||||||
class Bid(Table):
|
|
||||||
__tablename__ = "bids"
|
__tablename__ = "bids"
|
||||||
|
|
||||||
bid_id = Column("blob", primary_key=True)
|
bid_id = Column("blob", primary_key=True)
|
||||||
@@ -244,11 +253,7 @@ class Bid(Table):
|
|||||||
participate_txn_refund = Column("blob")
|
participate_txn_refund = Column("blob")
|
||||||
|
|
||||||
in_progress = Column("integer")
|
in_progress = Column("integer")
|
||||||
state = Column("integer")
|
|
||||||
state_time = Column("integer") # Timestamp of last state change
|
|
||||||
states = Column("blob") # Packed states and times
|
|
||||||
|
|
||||||
state_note = Column("string")
|
|
||||||
was_sent = Column("bool") # Sent by node
|
was_sent = Column("bool") # Sent by node
|
||||||
was_received = Column("bool")
|
was_received = Column("bool")
|
||||||
contract_count = Column("integer")
|
contract_count = Column("integer")
|
||||||
@@ -287,25 +292,13 @@ class Bid(Table):
|
|||||||
if self.isSet("participate_tx"):
|
if self.isSet("participate_tx"):
|
||||||
self.participate_tx.setState(new_state)
|
self.participate_tx.setState(new_state)
|
||||||
|
|
||||||
def setState(self, new_state, state_note=None):
|
|
||||||
now = int(time.time())
|
|
||||||
self.state = new_state
|
|
||||||
self.state_time = now
|
|
||||||
|
|
||||||
if self.isSet("state_note"):
|
|
||||||
self.state_note = state_note
|
|
||||||
if self.isSet("states") is False:
|
|
||||||
self.states = pack_state(new_state, now)
|
|
||||||
else:
|
|
||||||
self.states += pack_state(new_state, now)
|
|
||||||
|
|
||||||
def getLockTXBVout(self):
|
def getLockTXBVout(self):
|
||||||
if self.isSet("xmr_b_lock_tx"):
|
if self.isSet("xmr_b_lock_tx"):
|
||||||
return self.xmr_b_lock_tx.vout
|
return self.xmr_b_lock_tx.vout
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class SwapTx(Table):
|
class SwapTx(Table, StateRows):
|
||||||
__tablename__ = "transactions"
|
__tablename__ = "transactions"
|
||||||
|
|
||||||
bid_id = Column("blob")
|
bid_id = Column("blob")
|
||||||
@@ -328,21 +321,8 @@ class SwapTx(Table):
|
|||||||
block_height = Column("integer")
|
block_height = Column("integer")
|
||||||
block_time = Column("integer")
|
block_time = Column("integer")
|
||||||
|
|
||||||
state = Column("integer")
|
|
||||||
states = Column("blob") # Packed states and times
|
|
||||||
|
|
||||||
primary_key = PrimaryKeyConstraint("bid_id", "tx_type")
|
primary_key = PrimaryKeyConstraint("bid_id", "tx_type")
|
||||||
|
|
||||||
def setState(self, new_state):
|
|
||||||
if self.state == new_state:
|
|
||||||
return
|
|
||||||
self.state = new_state
|
|
||||||
now: int = int(time.time())
|
|
||||||
if self.isSet("states") is False:
|
|
||||||
self.states = pack_state(new_state, now)
|
|
||||||
else:
|
|
||||||
self.states += pack_state(new_state, now)
|
|
||||||
|
|
||||||
|
|
||||||
class PrefundedTx(Table):
|
class PrefundedTx(Table):
|
||||||
__tablename__ = "prefunded_transactions"
|
__tablename__ = "prefunded_transactions"
|
||||||
@@ -1059,7 +1039,7 @@ class DBMethods:
|
|||||||
if cursor is None:
|
if cursor is None:
|
||||||
self.closeDB(use_cursor, commit=False)
|
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:
|
if cursor is None:
|
||||||
raise ValueError("Cursor is null")
|
raise ValueError("Cursor is null")
|
||||||
if not hasattr(obj, "__tablename__"):
|
if not hasattr(obj, "__tablename__"):
|
||||||
@@ -1072,7 +1052,8 @@ class DBMethods:
|
|||||||
# See if the instance overwrote any class methods
|
# See if the instance overwrote any class methods
|
||||||
for mc in inspect.getmembers(obj.__class__):
|
for mc in inspect.getmembers(obj.__class__):
|
||||||
mc_name, mc_obj = mc
|
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__"):
|
if not hasattr(mc_obj, "__sqlite3_column__"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1113,6 +1094,7 @@ class DBMethods:
|
|||||||
order_by={},
|
order_by={},
|
||||||
query_suffix=None,
|
query_suffix=None,
|
||||||
extra_query_data={},
|
extra_query_data={},
|
||||||
|
columns_list=None,
|
||||||
):
|
):
|
||||||
if cursor is None:
|
if cursor is None:
|
||||||
raise ValueError("Cursor is null")
|
raise ValueError("Cursor is null")
|
||||||
@@ -1125,6 +1107,8 @@ class DBMethods:
|
|||||||
|
|
||||||
for mc in inspect.getmembers(table_class):
|
for mc in inspect.getmembers(table_class):
|
||||||
mc_name, mc_obj = mc
|
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__"):
|
if not hasattr(mc_obj, "__sqlite3_column__"):
|
||||||
continue
|
continue
|
||||||
if len(columns) > 0:
|
if len(columns) > 0:
|
||||||
@@ -1193,6 +1177,7 @@ class DBMethods:
|
|||||||
order_by={},
|
order_by={},
|
||||||
query_suffix=None,
|
query_suffix=None,
|
||||||
extra_query_data={},
|
extra_query_data={},
|
||||||
|
columns_list=None,
|
||||||
):
|
):
|
||||||
return firstOrNone(
|
return firstOrNone(
|
||||||
self.query(
|
self.query(
|
||||||
@@ -1202,10 +1187,11 @@ class DBMethods:
|
|||||||
order_by,
|
order_by,
|
||||||
query_suffix,
|
query_suffix,
|
||||||
extra_query_data,
|
extra_query_data,
|
||||||
|
columns_list,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateDB(self, obj, cursor, constraints=[]):
|
def updateDB(self, obj, cursor, constraints=[], columns_list=None):
|
||||||
if cursor is None:
|
if cursor is None:
|
||||||
raise ValueError("Cursor is null")
|
raise ValueError("Cursor is null")
|
||||||
if not hasattr(obj, "__tablename__"):
|
if not hasattr(obj, "__tablename__"):
|
||||||
@@ -1217,7 +1203,6 @@ class DBMethods:
|
|||||||
values = {}
|
values = {}
|
||||||
for mc in inspect.getmembers(obj.__class__):
|
for mc in inspect.getmembers(obj.__class__):
|
||||||
mc_name, mc_obj = mc
|
mc_name, mc_obj = mc
|
||||||
|
|
||||||
if not hasattr(mc_obj, "__sqlite3_column__"):
|
if not hasattr(mc_obj, "__sqlite3_column__"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1229,7 +1214,8 @@ class DBMethods:
|
|||||||
if mc_name in constraints:
|
if mc_name in constraints:
|
||||||
values[mc_name] = m_obj
|
values[mc_name] = m_obj
|
||||||
continue
|
continue
|
||||||
|
if columns_list is not None and mc_name not in columns_list:
|
||||||
|
continue
|
||||||
if len(values) > 0:
|
if len(values) > 0:
|
||||||
query += ", "
|
query += ", "
|
||||||
query += f"{mc_name} = :{mc_name}"
|
query += f"{mc_name} = :{mc_name}"
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
# Copyright (c) 2025 The Basicswap developers
|
# Copyright (c) 2025-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from basicswap.chainparams import (
|
from basicswap.chainparams import (
|
||||||
chainparams,
|
chainparams,
|
||||||
@@ -30,6 +31,7 @@ from basicswap.util.ecc import (
|
|||||||
)
|
)
|
||||||
from coincurve.dleag import verify_secp256k1_point
|
from coincurve.dleag import verify_secp256k1_point
|
||||||
from coincurve.keys import (
|
from coincurve.keys import (
|
||||||
|
PrivateKey,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -179,13 +181,16 @@ class CoinInterface:
|
|||||||
|
|
||||||
|
|
||||||
class AdaptorSigInterface:
|
class AdaptorSigInterface:
|
||||||
def getScriptLockTxDummyWitness(self, script: bytes):
|
def getP2WPKHDummyWitness(self) -> List[bytes]:
|
||||||
|
return [bytes(72), bytes(33)]
|
||||||
|
|
||||||
|
def getScriptLockTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [b"", bytes(72), bytes(72), bytes(len(script))]
|
return [b"", bytes(72), bytes(72), bytes(len(script))]
|
||||||
|
|
||||||
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes):
|
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [b"", bytes(72), bytes(72), bytes((1,)), bytes(len(script))]
|
return [b"", bytes(72), bytes(72), bytes((1,)), bytes(len(script))]
|
||||||
|
|
||||||
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes):
|
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [bytes(72), b"", bytes(len(script))]
|
return [bytes(72), b"", bytes(len(script))]
|
||||||
|
|
||||||
|
|
||||||
@@ -227,8 +232,7 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
|||||||
return pubkey.verify(sig, signed_hash, hasher=None)
|
return pubkey.verify(sig, signed_hash, hasher=None)
|
||||||
|
|
||||||
def sumKeys(self, ka: bytes, kb: bytes) -> bytes:
|
def sumKeys(self, ka: bytes, kb: bytes) -> bytes:
|
||||||
# TODO: Add to coincurve
|
return PrivateKey(ka).add(kb).secret
|
||||||
return i2b((b2i(ka) + b2i(kb)) % ep.o)
|
|
||||||
|
|
||||||
def sumPubkeys(self, Ka: bytes, Kb: bytes) -> bytes:
|
def sumPubkeys(self, Ka: bytes, Kb: bytes) -> bytes:
|
||||||
return PublicKey.combine_keys([PublicKey(Ka), PublicKey(Kb)]).format()
|
return PublicKey.combine_keys([PublicKey(Ka), PublicKey(Kb)]).format()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -828,7 +828,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
if self.isScriptP2WPKH(script):
|
if self.isScriptP2WPKH(script):
|
||||||
return [bytes(72), bytes(33)]
|
return self.getP2WPKHDummyWitness()
|
||||||
raise ValueError("Unknown script type")
|
raise ValueError("Unknown script type")
|
||||||
|
|
||||||
def createSCLockRefundTx(
|
def createSCLockRefundTx(
|
||||||
@@ -1943,9 +1943,16 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
raise ValueError("Unimplemented")
|
raise ValueError("Unimplemented")
|
||||||
|
|
||||||
def getWitnessStackSerialisedLength(self, witness_stack):
|
def getWitnessStackSerialisedLength(self, witness_stack):
|
||||||
length = getCompactSizeLen(len(witness_stack))
|
length: int = 0
|
||||||
for e in witness_stack:
|
if len(witness_stack) > 0 and isinstance(witness_stack[0], list):
|
||||||
length += getWitnessElementLen(len(e))
|
for input_stack in witness_stack:
|
||||||
|
length += getCompactSizeLen(len(input_stack))
|
||||||
|
for e in input_stack:
|
||||||
|
length += getWitnessElementLen(len(e))
|
||||||
|
else:
|
||||||
|
length += getCompactSizeLen(len(witness_stack))
|
||||||
|
for e in witness_stack:
|
||||||
|
length += getWitnessElementLen(len(e))
|
||||||
|
|
||||||
# See core SerializeTransaction
|
# See core SerializeTransaction
|
||||||
length += 1 # vinDummy
|
length += 1 # vinDummy
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ class PARTInterface(BTCInterface):
|
|||||||
|
|
||||||
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
def getScriptDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
if self.isScriptP2WPKH(script) or self.isScriptP2PKH(script):
|
if self.isScriptP2WPKH(script) or self.isScriptP2PKH(script):
|
||||||
return [bytes(72), bytes(33)]
|
return self.getP2WPKHDummyWitness()
|
||||||
raise ValueError("Unknown script type")
|
raise ValueError("Unknown script type")
|
||||||
|
|
||||||
def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str:
|
def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str:
|
||||||
@@ -146,9 +146,16 @@ class PARTInterface(BTCInterface):
|
|||||||
return encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey)
|
return encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey)
|
||||||
|
|
||||||
def getWitnessStackSerialisedLength(self, witness_stack) -> int:
|
def getWitnessStackSerialisedLength(self, witness_stack) -> int:
|
||||||
length: int = getCompactSizeLen(len(witness_stack))
|
length: int = 0
|
||||||
for e in witness_stack:
|
if len(witness_stack) > 0 and isinstance(witness_stack[0], list):
|
||||||
length += getWitnessElementLen(len(e))
|
for input_stack in witness_stack:
|
||||||
|
length += getCompactSizeLen(len(input_stack))
|
||||||
|
for e in input_stack:
|
||||||
|
length += getWitnessElementLen(len(e))
|
||||||
|
else:
|
||||||
|
length += getCompactSizeLen(len(witness_stack))
|
||||||
|
for e in witness_stack:
|
||||||
|
length += getWitnessElementLen(len(e))
|
||||||
return length
|
return length
|
||||||
|
|
||||||
def getWalletRestoreHeight(self) -> int:
|
def getWalletRestoreHeight(self) -> int:
|
||||||
|
|||||||
@@ -130,10 +130,7 @@ def redeemITx(self, bid_id: bytes, cursor):
|
|||||||
|
|
||||||
bid.initiate_tx.spend_txid = bytes.fromhex(txid)
|
bid.initiate_tx.spend_txid = bytes.fromhex(txid)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"Submitted initiate redeem txn %s to %s chain for bid %s",
|
f"Submitted initiate redeem txn {self.logIDT(txid)} to {ci_from.coin_name()} chain for bid {self.logIDB(bid_id)}"
|
||||||
txid,
|
|
||||||
ci_from.coin_name(),
|
|
||||||
bid_id.hex(),
|
|
||||||
)
|
)
|
||||||
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, "", cursor)
|
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, "", cursor)
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
lock_tx_vout=lock_tx_vout,
|
lock_tx_vout=lock_tx_vout,
|
||||||
)
|
)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Submitted lock B spend txn {self.log.id(txid)} to {ci_follower.coin_name()} chain for bid {self.log.id(bid_id)}."
|
f"Submitted lock B spend txn {self.logIDT(txid)} to {ci_follower.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id,
|
bid.bid_id,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ def describeBid(
|
|||||||
elif bid.state == BidStates.BID_ABANDONED:
|
elif bid.state == BidStates.BID_ABANDONED:
|
||||||
state_description = "Bid abandoned"
|
state_description = "Bid abandoned"
|
||||||
elif bid.state == BidStates.BID_ERROR:
|
elif bid.state == BidStates.BID_ERROR:
|
||||||
state_description = bid.state_note
|
state_description = "Bid error"
|
||||||
elif offer.swap_type == SwapTypes.XMR_SWAP:
|
elif offer.swap_type == SwapTypes.XMR_SWAP:
|
||||||
if bid.state == BidStates.BID_SENT:
|
if bid.state == BidStates.BID_SENT:
|
||||||
state_description = "Waiting for offerer to accept"
|
state_description = "Waiting for offerer to accept"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pyzmq==27.1.0
|
pyzmq==27.1.0
|
||||||
python-gnupg==0.5.5
|
python-gnupg==0.5.6
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.6
|
||||||
pycryptodome==3.23.0
|
pycryptodome==3.23.0
|
||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|||||||
@@ -122,9 +122,9 @@ pysocks==1.7.1 \
|
|||||||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
python-gnupg==0.5.5 \
|
python-gnupg==0.5.6 \
|
||||||
--hash=sha256:3fdcaf76f60a1b948ff8e37dc398d03cf9ce7427065d583082b92da7a4ff5a63 \
|
--hash=sha256:5743e96212d38923fc19083812dc127907e44dbd3bcf0db4d657e291d3c21eac \
|
||||||
--hash=sha256:51fa7b8831ff0914bc73d74c59b99c613de7247b91294323c39733bb85ac3fc1
|
--hash=sha256:b5050a55663d8ab9fcc8d97556d229af337a87a3ebebd7054cbd8b7e2043394a
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pyzmq==27.1.0 \
|
pyzmq==27.1.0 \
|
||||||
--hash=sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d \
|
--hash=sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d \
|
||||||
|
|||||||
@@ -1169,11 +1169,6 @@ def process_offers(args, config, script_state) -> None:
|
|||||||
)
|
)
|
||||||
use_rate = offer_template["minrate"]
|
use_rate = offer_template["minrate"]
|
||||||
|
|
||||||
# Final minimum rate check after all adjustments
|
|
||||||
if use_rate < offer_template["minrate"]:
|
|
||||||
print("Warning: Final rate clamping to minimum after all adjustments.")
|
|
||||||
use_rate = offer_template["minrate"]
|
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print(
|
print(
|
||||||
"Creating offer for: {} at rate: {}".format(
|
"Creating offer for: {} at rate: {}".format(
|
||||||
|
|||||||
@@ -622,6 +622,18 @@ class TestBase(unittest.TestCase):
|
|||||||
raise ValueError(f"wait_for_particl_height failed http_port: {http_port}")
|
raise ValueError(f"wait_for_particl_height failed http_port: {http_port}")
|
||||||
|
|
||||||
|
|
||||||
|
def run_process(client_id):
|
||||||
|
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
||||||
|
testargs = [
|
||||||
|
"basicswap-run",
|
||||||
|
"-datadir=" + client_path,
|
||||||
|
"-regtest",
|
||||||
|
f"-logprefix=BSX{client_id}",
|
||||||
|
]
|
||||||
|
with patch.object(sys, "argv", testargs):
|
||||||
|
runSystem.main()
|
||||||
|
|
||||||
|
|
||||||
class XmrTestBase(TestBase):
|
class XmrTestBase(TestBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -632,23 +644,12 @@ class XmrTestBase(TestBase):
|
|||||||
|
|
||||||
prepare_nodes(3, "monero")
|
prepare_nodes(3, "monero")
|
||||||
|
|
||||||
def run_thread(self, client_id):
|
|
||||||
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
|
||||||
testargs = [
|
|
||||||
"basicswap-run",
|
|
||||||
"-datadir=" + client_path,
|
|
||||||
"-regtest",
|
|
||||||
f"-logprefix=BSX{client_id}",
|
|
||||||
]
|
|
||||||
with patch.object(sys, "argv", testargs):
|
|
||||||
runSystem.main()
|
|
||||||
|
|
||||||
def start_processes(self):
|
def start_processes(self):
|
||||||
self.delay_event.clear()
|
self.delay_event.clear()
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
self.processes.append(
|
self.processes.append(
|
||||||
multiprocessing.Process(target=self.run_thread, args=(i,))
|
multiprocessing.Process(target=run_process, args=(i,))
|
||||||
)
|
)
|
||||||
self.processes[-1].start()
|
self.processes[-1].start()
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from tests.basicswap.common import (
|
|||||||
waitForNumSwapping,
|
waitForNumSwapping,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common_xmr import (
|
from tests.basicswap.common_xmr import (
|
||||||
|
run_process,
|
||||||
XmrTestBase,
|
XmrTestBase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ class Test(XmrTestBase):
|
|||||||
c1 = self.processes[1]
|
c1 = self.processes[1]
|
||||||
c1.terminate()
|
c1.terminate()
|
||||||
c1.join()
|
c1.join()
|
||||||
self.processes[1] = multiprocessing.Process(target=self.run_thread, args=(1,))
|
self.processes[1] = multiprocessing.Process(target=run_process, args=(1,))
|
||||||
self.processes[1].start()
|
self.processes[1].start()
|
||||||
|
|
||||||
waitForServer(self.delay_event, 12701)
|
waitForServer(self.delay_event, 12701)
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ if not len(logger.handlers):
|
|||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
|
|
||||||
|
def run_process(client_id):
|
||||||
|
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
||||||
|
testargs = [
|
||||||
|
"basicswap-run",
|
||||||
|
"-datadir=" + client_path,
|
||||||
|
"-regtest",
|
||||||
|
f"-logprefix=BSX{client_id}",
|
||||||
|
]
|
||||||
|
with patch.object(sys, "argv", testargs):
|
||||||
|
runSystem.main()
|
||||||
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -64,24 +76,13 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
run_prepare(i, client_path, bins_path, "monero,bitcoin", mnemonics[0])
|
run_prepare(i, client_path, bins_path, "monero,bitcoin", mnemonics[0])
|
||||||
|
|
||||||
def run_thread(self, client_id):
|
|
||||||
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
|
||||||
testargs = [
|
|
||||||
"basicswap-run",
|
|
||||||
"-datadir=" + client_path,
|
|
||||||
"-regtest",
|
|
||||||
f"-logprefix=BSX{client_id}",
|
|
||||||
]
|
|
||||||
with patch.object(sys, "argv", testargs):
|
|
||||||
runSystem.main()
|
|
||||||
|
|
||||||
def test_wallet(self):
|
def test_wallet(self):
|
||||||
update_thread = None
|
update_thread = None
|
||||||
processes = []
|
processes = []
|
||||||
|
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
|
processes.append(multiprocessing.Process(target=run_process, args=(i,)))
|
||||||
processes[-1].start()
|
processes[-1].start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -102,6 +102,18 @@ def prepare_node(node_id, mnemonic):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_process(client_id):
|
||||||
|
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
||||||
|
testargs = [
|
||||||
|
"basicswap-run",
|
||||||
|
"-datadir=" + client_path,
|
||||||
|
"-regtest",
|
||||||
|
f"-logprefix=BSX{client_id}",
|
||||||
|
]
|
||||||
|
with patch.object(sys, "argv", testargs):
|
||||||
|
runSystem.main()
|
||||||
|
|
||||||
|
|
||||||
class Test(TestBase):
|
class Test(TestBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -112,17 +124,6 @@ class Test(TestBase):
|
|||||||
for i in range(3):
|
for i in range(3):
|
||||||
cls.used_mnemonics.append(prepare_node(i, mnemonics[0] if i == 0 else None))
|
cls.used_mnemonics.append(prepare_node(i, mnemonics[0] if i == 0 else None))
|
||||||
|
|
||||||
def run_thread(self, client_id):
|
|
||||||
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
|
|
||||||
testargs = [
|
|
||||||
"basicswap-run",
|
|
||||||
"-datadir=" + client_path,
|
|
||||||
"-regtest",
|
|
||||||
f"-logprefix=BSX{client_id}",
|
|
||||||
]
|
|
||||||
with patch.object(sys, "argv", testargs):
|
|
||||||
runSystem.main()
|
|
||||||
|
|
||||||
def finalise(self, processes):
|
def finalise(self, processes):
|
||||||
self.delay_event.set()
|
self.delay_event.set()
|
||||||
if self.update_thread:
|
if self.update_thread:
|
||||||
@@ -136,7 +137,7 @@ class Test(TestBase):
|
|||||||
processes = []
|
processes = []
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
|
processes.append(multiprocessing.Process(target=run_process, args=(i,)))
|
||||||
processes[-1].start()
|
processes[-1].start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -201,7 +202,7 @@ class Test(TestBase):
|
|||||||
|
|
||||||
logging.info("Starting a new node on the same mnemonic as the first")
|
logging.info("Starting a new node on the same mnemonic as the first")
|
||||||
prepare_node(3, self.used_mnemonics[0])
|
prepare_node(3, self.used_mnemonics[0])
|
||||||
processes.append(multiprocessing.Process(target=self.run_thread, args=(3,)))
|
processes.append(multiprocessing.Process(target=run_process, args=(3,)))
|
||||||
processes[-1].start()
|
processes[-1].start()
|
||||||
waitForServer(self.delay_event, 12703)
|
waitForServer(self.delay_event, 12703)
|
||||||
|
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ def signal_handler(self, sig, frame):
|
|||||||
self.delay_event.set()
|
self.delay_event.set()
|
||||||
|
|
||||||
|
|
||||||
def run_thread(self, client_id):
|
def run_process(client_id):
|
||||||
client_path = os.path.join(test_path, "client{}".format(client_id))
|
client_path = os.path.join(test_path, "client{}".format(client_id))
|
||||||
testargs = [
|
testargs = [
|
||||||
"basicswap-run",
|
"basicswap-run",
|
||||||
@@ -288,11 +288,8 @@ def start_processes(self):
|
|||||||
for i in range(NUM_NODES):
|
for i in range(NUM_NODES):
|
||||||
self.processes.append(
|
self.processes.append(
|
||||||
multiprocessing.Process(
|
multiprocessing.Process(
|
||||||
target=run_thread,
|
target=run_process,
|
||||||
args=(
|
args=(i,),
|
||||||
self,
|
|
||||||
i,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.processes[-1].start()
|
self.processes[-1].start()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2021-2024 tecnovert
|
# Copyright (c) 2021-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -921,10 +921,10 @@ class BasicSwapTest(TestFunctions):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(BasicSwapTest, cls).setUpClass()
|
super(BasicSwapTest, cls).setUpClass()
|
||||||
if False:
|
|
||||||
for client in cls.swap_clients:
|
@classmethod
|
||||||
client.log.safe_logs = True
|
def addCoinSettings(cls, settings, datadir, node_id):
|
||||||
client.log.safe_logs_prefix = b"tests"
|
settings["fetchpricesthread"] = False
|
||||||
|
|
||||||
def test_001_nested_segwit(self):
|
def test_001_nested_segwit(self):
|
||||||
# p2sh-p2wpkh
|
# p2sh-p2wpkh
|
||||||
@@ -1509,6 +1509,23 @@ class BasicSwapTest(TestFunctions):
|
|||||||
vsize = tx_decoded["vsize"]
|
vsize = tx_decoded["vsize"]
|
||||||
expect_fee_int = round(self.test_fee_rate * vsize / 1000)
|
expect_fee_int = round(self.test_fee_rate * vsize / 1000)
|
||||||
|
|
||||||
|
tx_obj = ci.loadTx(lock_tx)
|
||||||
|
vsize_from_ci = ci.getTxVSize(tx_obj)
|
||||||
|
assert vsize == vsize_from_ci
|
||||||
|
tx_no_witness = tx_obj.serialize_without_witness()
|
||||||
|
|
||||||
|
dummy_witness_stack = []
|
||||||
|
for txi in tx_obj.vin:
|
||||||
|
dummy_witness_stack.append(ci.getP2WPKHDummyWitness())
|
||||||
|
witness_bytes_len_est: int = ci.getWitnessStackSerialisedLength(
|
||||||
|
dummy_witness_stack
|
||||||
|
)
|
||||||
|
tx_obj_no_witness = ci.loadTx(tx_no_witness)
|
||||||
|
vsize_estimated = ci.getTxVSize(
|
||||||
|
tx_obj_no_witness, add_witness_bytes=witness_bytes_len_est
|
||||||
|
)
|
||||||
|
assert vsize <= vsize_estimated and vsize_estimated - vsize < 4
|
||||||
|
|
||||||
out_value: int = 0
|
out_value: int = 0
|
||||||
for txo in tx_decoded["vout"]:
|
for txo in tx_decoded["vout"]:
|
||||||
if "value" in txo:
|
if "value" in txo:
|
||||||
@@ -1556,7 +1573,7 @@ class BasicSwapTest(TestFunctions):
|
|||||||
|
|
||||||
expect_vsize: int = ci.xmr_swap_a_lock_spend_tx_vsize()
|
expect_vsize: int = ci.xmr_swap_a_lock_spend_tx_vsize()
|
||||||
assert expect_vsize >= vsize_actual
|
assert expect_vsize >= vsize_actual
|
||||||
assert expect_vsize - vsize_actual < 10
|
assert expect_vsize - vsize_actual <= 10
|
||||||
|
|
||||||
# Test chain b (no-script) lock tx size
|
# Test chain b (no-script) lock tx size
|
||||||
v = ci.getNewRandomKey()
|
v = ci.getNewRandomKey()
|
||||||
@@ -1577,7 +1594,7 @@ class BasicSwapTest(TestFunctions):
|
|||||||
|
|
||||||
expect_vsize: int = ci.xmr_swap_b_lock_spend_tx_vsize()
|
expect_vsize: int = ci.xmr_swap_b_lock_spend_tx_vsize()
|
||||||
assert expect_vsize >= lock_tx_b_spend_decoded["vsize"]
|
assert expect_vsize >= lock_tx_b_spend_decoded["vsize"]
|
||||||
assert expect_vsize - lock_tx_b_spend_decoded["vsize"] < 10
|
assert expect_vsize - lock_tx_b_spend_decoded["vsize"] <= 10
|
||||||
|
|
||||||
def test_011_p2sh(self):
|
def test_011_p2sh(self):
|
||||||
# Not used in bsx for native-segwit coins
|
# Not used in bsx for native-segwit coins
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2024 tecnovert
|
# Copyright (c) 2019-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -67,6 +67,18 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ci_btc():
|
||||||
|
btc_coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
||||||
|
btc_coin_settings.update(REQUIRED_SETTINGS)
|
||||||
|
return BTCInterface(btc_coin_settings, "regtest")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ci_xmr():
|
||||||
|
xmr_coin_settings = {"rpcport": 0, "walletrpcport": 0, "walletrpcauth": "none"}
|
||||||
|
xmr_coin_settings.update(REQUIRED_SETTINGS)
|
||||||
|
return XMRInterface(xmr_coin_settings, "regtest")
|
||||||
|
|
||||||
def test_serialise_num(self):
|
def test_serialise_num(self):
|
||||||
def test_case(v, nb=None):
|
def test_case(v, nb=None):
|
||||||
b = SerialiseNum(v)
|
b = SerialiseNum(v)
|
||||||
@@ -86,10 +98,7 @@ class Test(unittest.TestCase):
|
|||||||
test_case(4194642)
|
test_case(4194642)
|
||||||
|
|
||||||
def test_sequence(self):
|
def test_sequence(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
time_val = 48 * 60 * 60
|
time_val = 48 * 60 * 60
|
||||||
encoded = ci.getExpectedSequence(TxLockTypes.SEQUENCE_LOCK_TIME, time_val)
|
encoded = ci.getExpectedSequence(TxLockTypes.SEQUENCE_LOCK_TIME, time_val)
|
||||||
@@ -197,10 +206,47 @@ class Test(unittest.TestCase):
|
|||||||
"5c26c518fb698e91a5858c33e9075488c55c235f391162fe9e6cbd4f694f80aa"
|
"5c26c518fb698e91a5858c33e9075488c55c235f391162fe9e6cbd4f694f80aa"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_key_summing(self):
|
||||||
|
ci_btc = self.ci_btc()
|
||||||
|
ci_xmr = self.ci_xmr()
|
||||||
|
keys = [
|
||||||
|
bytes.fromhex(
|
||||||
|
"e6b8e7c2ca3a88fe4f28591aa0f91fec340179346559e4ec430c2531aecc19aa"
|
||||||
|
),
|
||||||
|
bytes.fromhex(
|
||||||
|
"b725b6359bd2b510d9d5a7bba7bdee17abbf113253f6338ea50a8f0cf45fd0d0"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
sum_secp256k1: bytes = ci_btc.sumKeys(keys[0], keys[1])
|
||||||
|
assert (
|
||||||
|
sum_secp256k1.hex()
|
||||||
|
== "9dde9df8660d3e0f28fe00d648b70e052511ad800a07783f284455b1d2f5a939"
|
||||||
|
)
|
||||||
|
|
||||||
|
sum_ed25519: bytes = ci_xmr.sumKeys(keys[0], keys[1])
|
||||||
|
assert (
|
||||||
|
sum_ed25519.hex()
|
||||||
|
== "0dde9df8660d3e0f28fe00d648b70e0323e9c192fe9b94f1cf7138515e877725"
|
||||||
|
)
|
||||||
|
|
||||||
|
sum_secp256k1 = ci_btc.sumPubkeys(
|
||||||
|
ci_btc.getPubkey(keys[0]), ci_btc.getPubkey(keys[1])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
sum_secp256k1.hex()
|
||||||
|
== "028c30392e35620af0787b363a03cf9a695336759664436e1f609481c869541a5c"
|
||||||
|
)
|
||||||
|
|
||||||
|
sum_ed25519 = ci_xmr.sumPubkeys(
|
||||||
|
ci_xmr.getPubkey(keys[0]), ci_xmr.getPubkey(keys[1])
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
sum_ed25519.hex()
|
||||||
|
== "4b2dd2dc9acc9be7efed4fdbfb96f0002aeb9e4c8638c5b24562a7158b283626"
|
||||||
|
)
|
||||||
|
|
||||||
def test_ecdsa_otves(self):
|
def test_ecdsa_otves(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
vk_sign = ci.getNewRandomKey()
|
vk_sign = ci.getNewRandomKey()
|
||||||
vk_encrypt = ci.getNewRandomKey()
|
vk_encrypt = ci.getNewRandomKey()
|
||||||
|
|
||||||
@@ -209,21 +255,16 @@ class Test(unittest.TestCase):
|
|||||||
sign_hash = secrets.token_bytes(32)
|
sign_hash = secrets.token_bytes(32)
|
||||||
|
|
||||||
cipher_text = ecdsaotves_enc_sign(vk_sign, pk_encrypt, sign_hash)
|
cipher_text = ecdsaotves_enc_sign(vk_sign, pk_encrypt, sign_hash)
|
||||||
|
|
||||||
assert ecdsaotves_enc_verify(pk_sign, pk_encrypt, sign_hash, cipher_text)
|
assert ecdsaotves_enc_verify(pk_sign, pk_encrypt, sign_hash, cipher_text)
|
||||||
|
|
||||||
sig = ecdsaotves_dec_sig(vk_encrypt, cipher_text)
|
sig = ecdsaotves_dec_sig(vk_encrypt, cipher_text)
|
||||||
|
|
||||||
assert ci.verifySig(pk_sign, sign_hash, sig)
|
assert ci.verifySig(pk_sign, sign_hash, sig)
|
||||||
|
|
||||||
recovered_key = ecdsaotves_rec_enc_key(pk_encrypt, cipher_text, sig)
|
recovered_key = ecdsaotves_rec_enc_key(pk_encrypt, cipher_text, sig)
|
||||||
|
|
||||||
assert vk_encrypt == recovered_key
|
assert vk_encrypt == recovered_key
|
||||||
|
|
||||||
def test_sign(self):
|
def test_sign(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
vk = ci.getNewRandomKey()
|
vk = ci.getNewRandomKey()
|
||||||
pk = ci.getPubkey(vk)
|
pk = ci.getPubkey(vk)
|
||||||
@@ -236,9 +277,7 @@ class Test(unittest.TestCase):
|
|||||||
ci.verifySig(pk, message_hash, sig)
|
ci.verifySig(pk, message_hash, sig)
|
||||||
|
|
||||||
def test_sign_compact(self):
|
def test_sign_compact(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
vk = ci.getNewRandomKey()
|
vk = ci.getNewRandomKey()
|
||||||
pk = ci.getPubkey(vk)
|
pk = ci.getPubkey(vk)
|
||||||
@@ -251,9 +290,7 @@ class Test(unittest.TestCase):
|
|||||||
assert sig == sig2
|
assert sig == sig2
|
||||||
|
|
||||||
def test_sign_recoverable(self):
|
def test_sign_recoverable(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
vk = ci.getNewRandomKey()
|
vk = ci.getNewRandomKey()
|
||||||
pk = ci.getPubkey(vk)
|
pk = ci.getPubkey(vk)
|
||||||
@@ -267,18 +304,13 @@ class Test(unittest.TestCase):
|
|||||||
assert sig == sig2
|
assert sig == sig2
|
||||||
|
|
||||||
def test_pubkey_to_address(self):
|
def test_pubkey_to_address(self):
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
ci = self.ci_btc()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci = BTCInterface(coin_settings, "regtest")
|
|
||||||
pk = h2b("02c26a344e7d21bcc6f291532679559f2fd234c881271ff98714855edc753763a6")
|
pk = h2b("02c26a344e7d21bcc6f291532679559f2fd234c881271ff98714855edc753763a6")
|
||||||
addr = ci.pubkey_to_address(pk)
|
addr = ci.pubkey_to_address(pk)
|
||||||
assert addr == "mj6SdSxmWRmdDqR5R3FfZmRiLmQfQAsLE8"
|
assert addr == "mj6SdSxmWRmdDqR5R3FfZmRiLmQfQAsLE8"
|
||||||
|
|
||||||
def test_dleag(self):
|
def test_dleag(self):
|
||||||
coin_settings = {"rpcport": 0, "walletrpcport": 0, "walletrpcauth": "none"}
|
ci = self.ci_xmr()
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
|
|
||||||
ci = XMRInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
key = ci.getNewRandomKey()
|
key = ci.getNewRandomKey()
|
||||||
proof = ci.proveDLEAG(key)
|
proof = ci.proveDLEAG(key)
|
||||||
@@ -409,15 +441,8 @@ class Test(unittest.TestCase):
|
|||||||
amount_to_recreate = int((amount_from * rate) // (10**scale_from))
|
amount_to_recreate = int((amount_from * rate) // (10**scale_from))
|
||||||
assert "10.00000000" == format_amount(amount_to_recreate, scale_to)
|
assert "10.00000000" == format_amount(amount_to_recreate, scale_to)
|
||||||
|
|
||||||
coin_settings = {
|
ci_xmr = self.ci_xmr()
|
||||||
"rpcport": 0,
|
ci_btc = self.ci_btc()
|
||||||
"rpcauth": "none",
|
|
||||||
"walletrpcport": 0,
|
|
||||||
"walletrpcauth": "none",
|
|
||||||
}
|
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
|
||||||
ci_xmr = XMRInterface(coin_settings, "regtest")
|
|
||||||
ci_btc = BTCInterface(coin_settings, "regtest")
|
|
||||||
|
|
||||||
for i in range(10000):
|
for i in range(10000):
|
||||||
test_pairs = random.randint(0, 3)
|
test_pairs = random.randint(0, 3)
|
||||||
@@ -663,6 +688,7 @@ class Test(unittest.TestCase):
|
|||||||
ki.record_id = 1
|
ki.record_id = 1
|
||||||
ki.address = "test1"
|
ki.address = "test1"
|
||||||
ki.label = "test1"
|
ki.label = "test1"
|
||||||
|
ki.note = "note1"
|
||||||
try:
|
try:
|
||||||
db_test.add(ki, cursor, upsert=False)
|
db_test.add(ki, cursor, upsert=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -670,6 +696,65 @@ class Test(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Should have errored.")
|
raise ValueError("Should have errored.")
|
||||||
db_test.add(ki, cursor, upsert=True)
|
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:
|
finally:
|
||||||
db_test.closeDB(cursor)
|
db_test.closeDB(cursor)
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,18 @@ def updateThread():
|
|||||||
delay_event.wait(5)
|
delay_event.wait(5)
|
||||||
|
|
||||||
|
|
||||||
|
def run_process(client_id):
|
||||||
|
client_path = os.path.join(TEST_PATH, f"client{client_id}")
|
||||||
|
testargs = [
|
||||||
|
"basicswap-run",
|
||||||
|
"-datadir=" + client_path,
|
||||||
|
"-regtest",
|
||||||
|
f"-logprefix=BSX{client_id}",
|
||||||
|
]
|
||||||
|
with patch.object(sys, "argv", testargs):
|
||||||
|
runSystem.main()
|
||||||
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -76,17 +88,6 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
prepare_nodes(3, "bitcoin")
|
prepare_nodes(3, "bitcoin")
|
||||||
|
|
||||||
def run_thread(self, client_id):
|
|
||||||
client_path = os.path.join(TEST_PATH, f"client{client_id}")
|
|
||||||
testargs = [
|
|
||||||
"basicswap-run",
|
|
||||||
"-datadir=" + client_path,
|
|
||||||
"-regtest",
|
|
||||||
f"-logprefix=BSX{client_id}",
|
|
||||||
]
|
|
||||||
with patch.object(sys, "argv", testargs):
|
|
||||||
runSystem.main()
|
|
||||||
|
|
||||||
def wait_for_node_height(self, port=12701, wallet_ticker="part", wait_for_blocks=3):
|
def wait_for_node_height(self, port=12701, wallet_ticker="part", wait_for_blocks=3):
|
||||||
# Wait for height, or sequencelock is thrown off by genesis blocktime
|
# Wait for height, or sequencelock is thrown off by genesis blocktime
|
||||||
logging.info(
|
logging.info(
|
||||||
@@ -112,7 +113,7 @@ class Test(unittest.TestCase):
|
|||||||
processes = []
|
processes = []
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
|
processes.append(multiprocessing.Process(target=run_process, args=(i,)))
|
||||||
processes[-1].start()
|
processes[-1].start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -169,7 +170,7 @@ class Test(unittest.TestCase):
|
|||||||
c1 = processes[1]
|
c1 = processes[1]
|
||||||
c1.terminate()
|
c1.terminate()
|
||||||
c1.join()
|
c1.join()
|
||||||
processes[1] = multiprocessing.Process(target=self.run_thread, args=(1,))
|
processes[1] = multiprocessing.Process(target=run_process, args=(1,))
|
||||||
processes[1].start()
|
processes[1].start()
|
||||||
|
|
||||||
waitForServer(delay_event, 12701)
|
waitForServer(delay_event, 12701)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from tests.basicswap.common import (
|
|||||||
waitForNumBids,
|
waitForNumBids,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common_xmr import (
|
from tests.basicswap.common_xmr import (
|
||||||
|
run_process,
|
||||||
XmrTestBase,
|
XmrTestBase,
|
||||||
waitForBidState,
|
waitForBidState,
|
||||||
)
|
)
|
||||||
@@ -104,7 +105,7 @@ class Test(XmrTestBase):
|
|||||||
self.delay_event.wait(5)
|
self.delay_event.wait(5)
|
||||||
|
|
||||||
logger.info("Starting node 0")
|
logger.info("Starting node 0")
|
||||||
self.processes[0] = multiprocessing.Process(target=self.run_thread, args=(0,))
|
self.processes[0] = multiprocessing.Process(target=run_process, args=(0,))
|
||||||
self.processes[0].start()
|
self.processes[0].start()
|
||||||
|
|
||||||
waitForServer(self.delay_event, 12700)
|
waitForServer(self.delay_event, 12700)
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ from tests.basicswap.util import (
|
|||||||
waitForServer,
|
waitForServer,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common import (
|
from tests.basicswap.common import (
|
||||||
waitForNumOffers,
|
|
||||||
waitForNumBids,
|
waitForNumBids,
|
||||||
|
waitForNumOffers,
|
||||||
waitForNumSwapping,
|
waitForNumSwapping,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common_xmr import (
|
from tests.basicswap.common_xmr import (
|
||||||
|
run_process,
|
||||||
XmrTestBase,
|
XmrTestBase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -94,12 +95,13 @@ class Test(XmrTestBase):
|
|||||||
|
|
||||||
waitForNumBids(self.delay_event, 12700, 1)
|
waitForNumBids(self.delay_event, 12700, 1)
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(20):
|
||||||
bids = read_json_api(12700, "bids")
|
bids = read_json_api(12700, "bids")
|
||||||
bid = bids[0]
|
bid = bids[0]
|
||||||
if bid["bid_state"] == "Received":
|
if bid["bid_state"] == "Received":
|
||||||
break
|
break
|
||||||
self.delay_event.wait(1)
|
self.delay_event.wait(1)
|
||||||
|
assert bid["bid_state"] == "Received"
|
||||||
assert bid["expire_at"] == bid["created_at"] + data["validmins"] * 60
|
assert bid["expire_at"] == bid["created_at"] + data["validmins"] * 60
|
||||||
|
|
||||||
data = {"accept": True}
|
data = {"accept": True}
|
||||||
@@ -112,7 +114,7 @@ class Test(XmrTestBase):
|
|||||||
c1 = self.processes[1]
|
c1 = self.processes[1]
|
||||||
c1.terminate()
|
c1.terminate()
|
||||||
c1.join()
|
c1.join()
|
||||||
self.processes[1] = multiprocessing.Process(target=self.run_thread, args=(1,))
|
self.processes[1] = multiprocessing.Process(target=run_process, args=(1,))
|
||||||
self.processes[1].start()
|
self.processes[1].start()
|
||||||
|
|
||||||
waitForServer(self.delay_event, 12701)
|
waitForServer(self.delay_event, 12701)
|
||||||
|
|||||||
Reference in New Issue
Block a user