Compare commits

...

23 Commits

Author SHA1 Message Date
tecnovert 19f13d9d96 Merge pull request #481 from tecnovert/refactor
Refactor
2026-05-29 12:54:54 +00:00
tecnovert 27f9f8c13a doc: update release notes 2026-05-29 14:23:32 +02:00
tecnovert 248b8046b1 test: wait longer, add startup_delay option 2026-05-29 14:02:07 +02:00
tecnovert 6b4b97376b test: print xmr daemon logs on ci failure 2026-05-29 11:26:11 +02:00
tecnovert e8ebfd34d0 refactor: black 2026-05-28 01:55:36 +02:00
tecnovert 24c8e8b2dd refactor: remove duplicate method 2026-05-27 23:51:51 +02:00
tecnovert 7bf3dce974 Merge pull request #477 from kewde/patch-1
fix: public key validation
2026-05-27 11:12:28 +00:00
tecnovert b6e922e3a8 Merge pull request #478 from kewde/patch-2
fix: use main address for XMR & WOW
2026-05-27 11:11:26 +00:00
kewde 59be986aa4 fix: use main address for XMR & WOW 2026-05-26 14:12:19 +02:00
kewde 25dd3809e9 fix: public key validation
https://github.com/basicswap/coincurve/blob/2bf23f173f411a60c66ba973231fadab772bfed2/src/coincurve/dleag.py#L63
2026-05-26 14:04:43 +02:00
tecnovert be1dbaeeaa build, guix: update packed version 2026-05-08 20:45:39 +02:00
tecnovert 3b76adeedb build: raise version to 0.16.2 2026-05-08 20:30:19 +02:00
tecnovert ae6691e7ab Merge pull request #467 from tecnovert/refactor
refactor: simplify getAddressInfo
2026-05-08 18:26:13 +00:00
tecnovert 8482533b37 Merge pull request #471 from gerlofvanek/fix_balances
Fix: Wallet balance overwrite on WebSocket updates.
2026-05-08 18:18:17 +00:00
gerlofvanek 8fe0913fda BLACK 2026-05-08 19:59:42 +02:00
gerlofvanek 9244a9fed8 Fix: Wallet balance overwrite on WebSocket updates. 2026-05-08 19:54:22 +02:00
tecnovert bfc58955da Merge pull request #469 from gerlofvanek/fix_tests
Fixes: PIVX/FIRO and test_pivx.py
2026-05-08 17:45:41 +00:00
tecnovert f77b7dc363 Merge pull request #470 from tecnovert/ci
test: show log on failure
2026-05-08 17:45:23 +00:00
tecnovert a6b5906a6d test: add balance check to test_swap_direction.py 2026-05-08 19:22:41 +02:00
tecnovert 568eab1f31 test: show log on failure 2026-05-08 18:31:27 +02:00
gerlofvanek 1b86df9b60 Fix: PIVX/Firo bug and test_pivx.py 2026-05-08 17:10:27 +02:00
tecnovert c8e7c02fe2 refactor: reduce wallet_manager imports 2026-05-04 19:39:03 +02:00
tecnovert 57a1a6505e refactor: simplify getAddressInfo 2026-05-04 19:24:25 +02:00
43 changed files with 257 additions and 229 deletions
+16
View File
@@ -94,13 +94,24 @@ jobs:
export XMR_BINDIR="$BIN_DIR/monero"
pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx"
- name: Run test_encrypted_xmr_reload
id: test_encrypted_xmr_reload
run: |
export PYTHONPATH=$(pwd)
export TEST_PATH=${TEST_RELOAD_PATH}
mkdir -p ${TEST_PATH}/bin
cp -r $BIN_DIR/* ${TEST_PATH}/bin/
pytest tests/basicswap/extended/test_encrypted_xmr_reload.py
- name: Print log files on failure
if: ${{ failure() && steps.test_encrypted_xmr_reload.conclusion == 'failure' }}
run: |
for i in 0 1 2; do
for logname in core_stderr core_stdout wallet_stderr wallet_stdout; do
echo "=== client${i} ${logname}.log ==="
cat /tmp/test_basicswap/client${i}/monero/${logname}.log || true
done
done
- name: Run selenium tests
id: selenium_tests
run: |
export TEST_PATH=/tmp/test_persistent
mkdir -p ${TEST_PATH}/bin
@@ -126,3 +137,8 @@ jobs:
echo "Running test_swap_direction.py"
python tests/basicswap/selenium/test_swap_direction.py
kill $TEST_NETWORK_PID
- name: Print log file on failure
if: ${{ failure() && steps.selenium_tests.conclusion == 'failure' }}
run: |
echo "=== SELENIUM BACKGROUND LOG ==="
cat /tmp/log.txt
+1 -1
View File
@@ -1,3 +1,3 @@
name = "basicswap"
__version__ = "0.16.1"
__version__ = "0.16.2"
+7 -2
View File
@@ -365,8 +365,10 @@ class BaseApp(DBMethods):
self.log.warning(f"Setting mocktime to {new_offset}")
self.mock_time_offset = new_offset
def get_int_setting(self, name: str, default_v: int, min_v: int, max_v) -> int:
value: int = self.settings.get(name, default_v)
def get_clamped_int_from(
self, settings: dict, name: str, default_v: int, min_v: int, max_v
) -> int:
value: int = settings.get(name, default_v)
if value < min_v:
self.log.warning(f"Setting {name} to {min_v}")
value = min_v
@@ -375,6 +377,9 @@ class BaseApp(DBMethods):
value = max_v
return value
def get_int_setting(self, name: str, default_v: int, min_v: int, max_v) -> int:
return self.get_clamped_int_from(self.settings, name, default_v, min_v, max_v)
def get_delay_event_seconds(self):
if self.min_delay_event == self.max_delay_event:
return self.min_delay_event
+24 -22
View File
@@ -166,7 +166,6 @@ import basicswap.network.network as bsn
import basicswap.protocols.atomic_swap_1 as atomic_swap_1
import basicswap.protocols.xmr_swap_1 as xmr_swap_1
PROTOCOL_VERSION_SECRET_HASH = 5
MINPROTO_VERSION_SECRET_HASH = 4
@@ -441,9 +440,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
self.check_delayed_auto_accept_seconds = self.get_int_setting(
"check_delayed_auto_accept_seconds", 60, 1, 20 * 60
)
self.startup_tries = self.get_int_setting(
"startup_tries", 15, 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_actions = 0
@@ -1618,13 +1614,22 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
# systemd will try to restart the process if fail_code != 0
self.stopRunning(1)
startup_tries = self.startup_tries
chain_client_settings = self.getChainClientSettings(coin_type)
if "startup_tries" in chain_client_settings:
startup_tries = chain_client_settings["startup_tries"]
if startup_tries < 1:
self.log.warning('"startup_tries" can\'t be less than 1.')
startup_tries = 1
# Total seconds waited for will be ((startup_tries(1 + startup_tries) / 2) * startup_delay
startup_tries: int = self.get_clamped_int_from(
chain_client_settings,
"startup_tries",
self.get_int_setting("startup_tries", 15, 1, 100),
1,
100,
)
startup_delay: int = self.get_clamped_int_from(
chain_client_settings,
"startup_delay",
self.get_int_setting("startup_delay", 5, 1, 100),
1,
100,
)
for i in range(startup_tries):
if self.delay_event.is_set():
return
@@ -1632,6 +1637,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
self.coin_clients[coin_type]["interface"].testDaemonRPC(with_wallet)
return
except Exception as ex:
wait_for: int = startup_delay * (1 + i)
if any(
log in str(ex)
for log in [
@@ -1644,13 +1650,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
]
):
self.log.info(
f"Waiting for {Coins(coin_type).name} RPC. Trying again in {5 * (1 + i)} seconds, {1 + i}/{startup_tries}."
f"Waiting for {Coins(coin_type).name} RPC. Trying again in {wait_for} seconds, {1 + i}/{startup_tries}."
)
else:
self.log.warning(
f"Can't connect to {Coins(coin_type).name} RPC: {ex}. Trying again in {5 * (1 + i)} seconds, {1 + i}/{startup_tries}."
f"Can't connect to {Coins(coin_type).name} RPC: {ex}. Trying again in {wait_for} seconds, {1 + i}/{startup_tries}."
)
self.delay_event.wait(5 * (1 + i))
self.delay_event.wait(wait_for)
self.log.error(f"Can't connect to {Coins(coin_type).name} RPC, exiting.")
self.stopRunning(1) # systemd will try to restart the process if fail_code != 0
@@ -3965,7 +3971,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
def isValidSwapDest(self, ci, dest: bytes):
ensure(isinstance(dest, bytes), "Swap destination must be bytes")
if ci.coin_type() in (Coins.PART_BLIND,):
return ci.isValidPubkey(dest)
return ci.verifyPubkey(dest)
# TODO: allow p2wsh
return ci.isValidAddressHash(dest)
@@ -10905,7 +10911,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
)
ensure(
ci_from.isValidAddressHash(bid_data.dest_af)
or ci_from.isValidPubkey(bid_data.dest_af),
or ci_from.verifyPubkey(bid_data.dest_af),
"Invalid destination address",
)
@@ -11896,7 +11902,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
)
vkbs = ci_to.sumKeys(kbsl, kbsf)
if coin_to == (Coins.XMR, Coins.WOW):
if coin_to in (Coins.XMR, Coins.WOW):
address_to = self.getCachedMainWalletAddress(ci_to, cursor)
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
address_to = self.getCachedStealthAddressForCoin(coin_to, cursor)
@@ -13811,8 +13817,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
num_watched_outputs += len(v["watched_outputs"])
now: int = self.getTime()
q_bids_str: str = (
"""SELECT
q_bids_str: str = """SELECT
COUNT(CASE WHEN b.was_sent THEN 1 ELSE NULL END) AS count_sent,
COUNT(CASE WHEN b.was_sent AND (s.in_progress OR (s.swap_ended = 0 AND b.expire_at > :now AND o.expire_at > :now)) THEN 1 ELSE NULL END) AS count_sent_active,
COUNT(CASE WHEN b.was_received THEN 1 ELSE NULL END) AS count_received,
@@ -13822,15 +13827,12 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
JOIN offers o ON b.offer_id = o.offer_id
JOIN bidstates s ON b.state = s.state_id
WHERE b.active_ind = 1"""
)
q_offers_str: str = (
"""SELECT
q_offers_str: str = """SELECT
COUNT(CASE WHEN expire_at > :now THEN 1 ELSE NULL END) AS count_active,
COUNT(CASE WHEN was_sent THEN 1 ELSE NULL END) AS count_sent,
COUNT(CASE WHEN was_sent AND expire_at > :now THEN 1 ELSE NULL END) AS count_sent_active
FROM offers WHERE active_ind = 1"""
)
try:
cursor = self.openDB()
-1
View File
@@ -12,7 +12,6 @@ import time
from enum import IntEnum, auto
from typing import Optional
CURRENT_DB_VERSION = 34
CURRENT_DB_DATA_VERSION = 8
-1
View File
@@ -7,7 +7,6 @@
import json
default_coingecko_api_key = "CG-8hm3r9iLfpEXv4ied8oLbeUj"
-7
View File
@@ -220,13 +220,6 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
if hash_len == 20:
return True
def isValidPubkey(self, pubkey: bytes) -> bool:
try:
self.verifyPubkey(pubkey)
return True
except Exception:
return False
def verifySig(self, pubkey: bytes, signed_hash: bytes, sig: bytes) -> bool:
pubkey = PublicKey(pubkey)
return pubkey.verify(sig, signed_hash, hasher=None)
+10 -17
View File
@@ -99,7 +99,6 @@ from basicswap.basicswap_util import TxLockTypes
from basicswap.chainparams import Coins
from basicswap.rpc import make_rpc_func, openrpc
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22
SEQUENCE_LOCKTIME_MASK = 0x0000FFFF
@@ -939,32 +938,26 @@ class BTCInterface(Secp256k1Interface):
if wm:
info = wm.getAddressInfo(self.coin_type(), address)
if info:
if or_watch_only:
return True
if or_watch_only is False and info["is_watch_only"] is True:
return False
return True
return False
try:
addr_info = self.rpc_wallet("getaddressinfo", [address])
if not or_watch_only:
if addr_info["ismine"]:
return True
else:
if or_watch_only is False:
return False
if addr_info["iswatchonly"]:
return True
if self._use_descriptors:
addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
if addr_info["ismine"] or addr_info["iswatchonly"]:
wo_addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
if wo_addr_info["iswatchonly"]:
return True
except Exception as e:
self._log.debug(f"isAddressMine RPC check failed: {e}")
wm = self.getWalletManager()
if wm:
info = wm.getAddressInfo(self.coin_type(), address)
if info:
if or_watch_only:
return True
return True
return False
def checkAddressMine(self, address: str) -> None:
@@ -1083,8 +1076,8 @@ class BTCInterface(Secp256k1Interface):
return self.encode_p2wsh(script)
def getDestForAddress(self, address: str) -> bytes:
bech32_prefix = self.chainparams_network()["hrp"]
if address.startswith(bech32_prefix + "1"):
bech32_prefix: str | None = self.chainparams_network().get("hrp", None)
if bech32_prefix and address.startswith(bech32_prefix + "1"):
_, witprog = segwit_addr.decode(bech32_prefix, address)
return CScript([OP_0, bytes(witprog)])
-1
View File
@@ -82,7 +82,6 @@ from coincurve.ecdsaotves import (
ecdsaotves_rec_enc_key,
)
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22
SEQUENCE_LOCKTIME_MASK = 0x0000F
+1 -1
View File
@@ -13,7 +13,7 @@ import subprocess
def createDCRWallet(args, hex_seed, logging, delay_event):
logging.info("Creating DCR wallet")
(pipe_r, pipe_w) = os.pipe() # subprocess.PIPE is buffered, blocks when read
pipe_r, pipe_w = os.pipe() # subprocess.PIPE is buffered, blocks when read
if os.name == "nt":
str_args = " ".join(args)
+1 -1
View File
@@ -361,7 +361,7 @@ class FIROInterface(BTCInterface):
)
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
def signTxWithKey(self, tx: bytes, key: bytes, prev_amount=None) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc(
"signrawtransaction",
+7 -23
View File
@@ -12,7 +12,7 @@ from .btc import BTCInterface
from basicswap.rpc import make_rpc_func
from basicswap.chainparams import Coins
from basicswap.util.address import decodeAddress
from .contrib.pivx_test_framework.messages import CBlock, ToHex, FromHex, CTransaction
from .contrib.pivx_test_framework.messages import CTransaction
from basicswap.contrib.test_framework.script import (
CScript,
OP_DUP,
@@ -100,29 +100,13 @@ class PIVXInterface(BTCInterface):
return decodeAddress(address)[1:]
def getBlockWithTxns(self, block_hash):
# TODO: Bypass decoderawtransaction and getblockheader
block = self.rpc("getblock", [block_hash, False])
block_header = self.rpc("getblockheader", [block_hash])
decoded_block = CBlock()
decoded_block = FromHex(decoded_block, block)
block = self.rpc("getblock", [block_hash, True])
tx_rv = []
for tx in decoded_block.vtx:
tx_dec = self.rpc("decoderawtransaction", [ToHex(tx)])
for txid_str in block["tx"]:
tx_dec = self.rpc("getrawtransaction", [txid_str, True])
tx_rv.append(tx_dec)
block_rv = {
"hash": block_hash,
"previousblockhash": block_header["previousblockhash"],
"tx": tx_rv,
"confirmations": block_header["confirmations"],
"height": block_header["height"],
"time": block_header["time"],
"version": block_header["version"],
"merkleroot": block_header["merkleroot"],
}
return block_rv
block["tx"] = tx_rv
return block
def withdrawCoin(self, value, addr_to, subfee):
params = [addr_to, value, "", "", subfee]
@@ -150,7 +134,7 @@ class PIVXInterface(BTCInterface):
)
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
def signTxWithKey(self, tx: bytes, key: bytes, prev_amount=None) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc(
"signrawtransaction",
-1
View File
@@ -34,7 +34,6 @@ from basicswap.rpc_xmr import make_xmr_rpc_func, make_xmr_rpc2_func
from basicswap.chainparams import XMR_COIN, Coins
from basicswap.interface.base import CoinInterface
ed25519_l = 2**252 + 27742317777372353535851937790883648493
+22
View File
@@ -192,6 +192,28 @@ def js_walletbalances(self, url_split, post_string, is_json) -> bytes:
coin_entry["electrum_synced"] = sync_status.get("synced", False)
coin_entry["electrum_height"] = sync_status.get("height", 0)
if k in wallets:
w = wallets[k]
if "error" not in w and "no_data" not in w:
if k == Coins.PART:
for field in ("blind_balance", "anon_balance"):
if field in w:
raw = w[field]
if isinstance(raw, float):
coin_entry[field] = f"{raw:.8f}".rstrip(
"0"
).rstrip(".")
elif isinstance(raw, int):
coin_entry[field] = str(raw)
else:
coin_entry[field] = raw
elif k == Coins.LTC:
if "mweb_balance" in w:
coin_entry["mweb_balance"] = w["mweb_balance"]
elif k == Coins.FIRO:
if "spark_balance" in w:
coin_entry["spark_balance"] = w["spark_balance"]
coins_with_balances.append(coin_entry)
if k == Coins.PART:
-1
View File
@@ -23,7 +23,6 @@ protobuf ParseFromString would reset the whole object, from_bytes won't.
from basicswap.util.integer import encode_varint, decode_varint
NPBW_INT = 0
NPBW_BYTES = 2
-1
View File
@@ -39,7 +39,6 @@ from basicswap.contrib.rfc6979 import (
rfc6979_hmac_sha256_generate,
)
START_TOKEN = 0xABCD
MSG_START_TOKEN = START_TOKEN.to_bytes(2, "big")
+1 -1
View File
@@ -53,7 +53,7 @@ def initSimplexClient(args, logger, delay_event):
# TODO: Must be a better way?
logger.info("Initialising Simplex client")
(pipe_r, pipe_w) = os.pipe() # subprocess.PIPE is buffered, blocks when read
pipe_r, pipe_w = os.pipe() # subprocess.PIPE is buffered, blocks when read
if os.name == "nt":
str_args = " ".join(args)
+8 -5
View File
@@ -351,16 +351,19 @@
);
matchingCoins.forEach(coinData => {
const balanceElements = document.querySelectorAll('.coinname-value[data-coinname]');
const balanceElements = document.querySelectorAll('.coinname-value[data-coinname][data-balance-type]');
balanceElements.forEach(element => {
const elementCoinName = element.getAttribute('data-coinname');
if (elementCoinName === coinData.name) {
const currentText = element.textContent;
const balanceType = element.getAttribute('data-balance-type');
const value = coinData[balanceType];
if (value !== undefined) {
const ticker = coinData.ticker || coinId.toUpperCase();
const newBalance = `${coinData.balance} ${ticker}`;
if (currentText !== newBalance) {
const newBalance = balanceType === 'est_fee' ? value : `${value} ${ticker}`;
if (element.textContent !== newBalance) {
element.textContent = newBalance;
console.log(`Updated balance: ${coinData.name} -> ${newBalance}`);
console.log(`Updated ${balanceType}: ${coinData.name} -> ${newBalance}`);
}
}
}
});
+19
View File
@@ -75,9 +75,28 @@
if (coinData.pending && parseFloat(coinData.pending) > 0) {
this.updatePendingBalance('Particl', 'Blind Balance:', coinData.pending, coinData.ticker || 'PART', 'Blind Unconfirmed:', coinData);
}
} else if (coinData.name === 'Litecoin MWEB') {
this.updateSpecificBalance('Litecoin', 'MWEB Balance:', coinData.balance, coinData.ticker || 'LTC');
this.removePendingBalance('Litecoin', 'MWEB Balance:');
if (coinData.pending && parseFloat(coinData.pending) > 0) {
this.updatePendingBalance('Litecoin', 'MWEB Balance:', coinData.pending, coinData.ticker || 'LTC', 'MWEB Pending:', coinData);
}
} else {
this.updateSpecificBalance(coinData.name, 'Balance:', coinData.balance, coinData.ticker || coinData.name);
if (coinData.mweb_balance !== undefined) {
this.updateSpecificBalance(coinData.name, 'MWEB Balance:', coinData.mweb_balance, coinData.ticker || coinData.name);
}
if (coinData.spark_balance !== undefined) {
this.updateSpecificBalance(coinData.name, 'Spark Balance:', coinData.spark_balance, coinData.ticker || coinData.name);
}
if (coinData.blind_balance !== undefined) {
this.updateSpecificBalance(coinData.name, 'Blind Balance:', coinData.blind_balance, coinData.ticker || coinData.name);
}
if (coinData.anon_balance !== undefined) {
this.updateSpecificBalance(coinData.name, 'Anon Balance:', coinData.anon_balance, coinData.ticker || coinData.name);
}
if (coinData.name !== 'Particl Anon' && coinData.name !== 'Particl Blind' && coinData.name !== 'Litecoin MWEB') {
if (coinData.pending && parseFloat(coinData.pending) > 0) {
this.updatePendingDisplay(coinData);
+14 -23
View File
@@ -138,7 +138,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Balance: </td>
<td class="py-3 px-6 bold">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="balance">{{ w.balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.pending %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.pending }} {{ w.ticker }} </span>
@@ -152,7 +152,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Blind"> </span>Blind Balance: </td>
<td class="py-3 px-6 bold">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="blind_balance">{{ w.blind_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.blind_unconfirmed %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Unconfirmed: +{{ w.blind_unconfirmed }} {{ w.ticker }}</span>
@@ -162,7 +162,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Anon"> </span>Anon Balance: </td>
<td class="py-3 px-6 bold">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="anon_balance">{{ w.anon_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.anon_pending %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.anon_pending }} {{ w.ticker }}</span>
@@ -177,7 +177,7 @@
{% if is_electrum_mode %}
<span class="text-gray-400 dark:text-gray-300">Not available in light mode</span>
{% else %}
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="mweb_balance">{{ w.mweb_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.mweb_pending %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.mweb_pending }} {{ w.ticker }} </span>
@@ -200,7 +200,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Spark"> </span>Spark Balance: </td>
<td class="py-3 px-6 bold">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.spark_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="spark_balance">{{ w.spark_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.spark_pending %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.spark_pending }} {{ w.ticker }} </span>
@@ -211,7 +211,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Spark"> </span>Spark Balance: </td>
<td class="py-3 px-6 bold">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.spark_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="spark_balance">{{ w.spark_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% if w.spark_pending %}
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.spark_pending }} {{ w.ticker }} </span>
@@ -348,16 +348,7 @@
<td class="py-3 px-6">{{ w.expected_seed }}</td>
</tr>
{% endif %}
{% if w.account_key %}
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
<td class="py-3 px-6 bold">Extended Private Key:</td>
<td class="py-3 px-6">
<span id="account-key-hidden" class="font-mono text-sm">••••••••••••••••</span>
<span id="account-key-value" class="font-mono text-sm hidden break-all">{{ w.account_key }}</span>
<button type="button" id="toggle-account-key" onclick="var h=document.getElementById('account-key-hidden'),v=document.getElementById('account-key-value');if(v.classList.contains('hidden')){v.classList.remove('hidden');h.classList.add('hidden');this.textContent='Hide';}else{v.classList.add('hidden');h.classList.remove('hidden');this.textContent='Show';}" class="ml-2 px-2 py-1 text-xs bg-blue-500 hover:bg-blue-600 text-white rounded">Show</button>
</td>
</tr>
{% endif %}
</table>
</div>
</div>
@@ -556,7 +547,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Balance: </td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="balance">{{ w.balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
</td>
</tr>
@@ -568,7 +559,7 @@
{% if is_electrum_mode %}
<span class="text-gray-400 dark:text-gray-300">Not available in light mode</span>
{% else %}
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="mweb_balance">{{ w.mweb_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
{% endif %}
</td>
@@ -578,7 +569,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-4 pl-6 bold w-1/4"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Spark Balance: </td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.spark_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="spark_balance">{{ w.spark_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
</td>
</tr>
@@ -587,7 +578,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-4 pl-6 bold w-1/4"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Spark Balance: </td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.spark_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="spark_balance">{{ w.spark_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
</td>
</tr>
@@ -596,14 +587,14 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Blind Balance: </td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="blind_balance">{{ w.blind_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
</td>
</tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Anon Balance: </td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="anon_balance">{{ w.anon_balance }} {{ w.ticker }}</span>
(<span class="usd-value"></span>)
</td>
</tr>
@@ -778,7 +769,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">Fee Estimate:</td>
<td class="py-3 px-6">
<span class="coinname-value" data-coinname="{{ w.name }}">{{ w.est_fee }}</span>
<span class="coinname-value" data-coinname="{{ w.name }}" data-balance-type="est_fee">{{ w.est_fee }}</span>
(<span class="usd-value fee-estimate-usd" data-decimals="8"></span>)
</td>
</tr>
+5 -5
View File
@@ -65,7 +65,7 @@
<div class="p-6 bg-coolGray-100 dark:bg-gray-600">
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Balance:</h4>
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }}</div>
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}" data-balance-type="balance">{{ w.balance }} {{ w.ticker }}</div>
</div>
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white ">{{ w.ticker }} USD value:</h4>
@@ -90,7 +90,7 @@
{% if w.cid == '1' %} {# PART #}
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Blind Balance:</h4>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }}</span>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}" data-balance-type="blind_balance">{{ w.blind_balance }} {{ w.ticker }}</span>
</div>
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Blind USD value:</h4>
@@ -108,7 +108,7 @@
{% endif %}
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Anon Balance:</h4>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }}</span>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}" data-balance-type="anon_balance">{{ w.anon_balance }} {{ w.ticker }}</span>
</div>
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Anon USD value:</h4>
@@ -129,7 +129,7 @@
{% if w.cid == '3' and w.connection_type != 'electrum' %} {# LTC - MWEB not available in electrum mode #}
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">MWEB Balance:</h4>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }}</span>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}" data-balance-type="mweb_balance">{{ w.mweb_balance }} {{ w.ticker }}</span>
</div>
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">MWEB USD value:</h4>
@@ -151,7 +151,7 @@
{% if w.cid == '13' %} {# FIRO #}
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Spark Balance:</h4>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.spark_balance }} {{ w.ticker }}</span>
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}" data-balance-type="spark_balance">{{ w.spark_balance }} {{ w.ticker }}</span>
</div>
<div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Spark USD value:</h4>
-1
View File
@@ -10,7 +10,6 @@ import json
import time
import decimal
COIN = 100000000
-1
View File
@@ -25,7 +25,6 @@ from basicswap.contrib.test_framework.messages import (
uint256_from_str,
)
AES_BLOCK_SIZE = 16
+9 -47
View File
@@ -4,10 +4,12 @@
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import hashlib
import json
import sqlite3
import threading
import time
from typing import Dict, List, Optional, Tuple
from coincurve import PrivateKey, PublicKey
from .chainparams import Coins
from .contrib.test_framework import segwit_addr
@@ -19,7 +21,7 @@ from .db_wallet import (
WalletTxCache,
WalletWatchOnly,
)
from .util.crypto import hash160
from .util.crypto import hash160, sha256
from .util.extkey import ExtKeyPair
@@ -112,13 +114,11 @@ class WalletManager:
def _deriveAddress(
self, coin_type: Coins, index: int, internal: bool = False
) -> Tuple[str, str, bytes]:
from coincurve import PublicKey
key = self._deriveKey(coin_type, index, internal)
pubkey = PublicKey.from_secret(key).format()
pkh = hash160(pubkey)
address = segwit_addr.encode(self._getHRP(coin_type), 0, pkh)
scripthash = hashlib.sha256(bytes([0x00, 0x14]) + pkh).digest()[::-1].hex()
scripthash = sha256(bytes([0x00, 0x14]) + pkh)[::-1].hex()
return address, scripthash, pubkey
def _syncStateIndices(self, coin_type: Coins, cursor) -> None:
@@ -275,8 +275,6 @@ class WalletManager:
def getAddress(
self, coin_type: Coins, index: int, internal: bool = False
) -> Optional[str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -395,8 +393,6 @@ class WalletManager:
include_watch_only: bool = True,
funded_only: bool = False,
) -> List[str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -426,8 +422,6 @@ class WalletManager:
return []
def getFundedAddresses(self, coin_type: Coins) -> Dict[str, str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -452,8 +446,6 @@ class WalletManager:
return {}
def getExistingInternalAddress(self, coin_type: Coins) -> Optional[str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -517,8 +509,6 @@ class WalletManager:
self._swap_client.closeDB(cursor, commit=False)
def getAddressInfo(self, coin_type: Coins, address: str) -> Optional[dict]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -559,8 +549,6 @@ class WalletManager:
return None
def getCachedTotalBalance(self, coin_type: Coins) -> int:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -700,8 +688,6 @@ class WalletManager:
if not self.isInitialized(coin_type):
return None
import sqlite3
now = int(time.time())
min_cache_time = now - max_cache_age
@@ -744,8 +730,6 @@ class WalletManager:
return None
def hasCachedBalances(self, coin_type: Coins, max_cache_age: int = 120) -> bool:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -762,8 +746,6 @@ class WalletManager:
def getPrivateKey(self, coin_type: Coins, address: str) -> Optional[bytes]:
if not self.isInitialized(coin_type):
return None
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -788,8 +770,6 @@ class WalletManager:
return None
def getSignableAddresses(self, coin_type: Coins) -> Dict[str, str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -861,10 +841,8 @@ class WalletManager:
label: str = "",
source: str = "import",
) -> bool:
from coincurve import PublicKey as CCPublicKey
try:
pubkey = CCPublicKey.from_secret(private_key).format()
pubkey = PublicKey.from_secret(private_key).format()
if (
segwit_addr.encode(self._getHRP(coin_type), 0, hash160(pubkey))
!= address
@@ -1003,7 +981,7 @@ class WalletManager:
def _b58decode_check(self, s: str) -> bytes:
data = self._b58decode(s)
payload, checksum = data[:-4], data[-4:]
expected = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
expected = sha256(sha256(payload))[:4]
if checksum != expected:
raise ValueError("Invalid base58 checksum")
return payload
@@ -1025,7 +1003,7 @@ class WalletManager:
master_key = self._master_keys.get(coin_type)
if master_key is None:
raise ValueError(f"Wallet not initialized for {coin_type}")
return hashlib.sha256(master_key + b"_import_key").digest()
return sha256(master_key + b"_import_key")
def _encryptPrivateKey(self, private_key: bytes, coin_type: Coins) -> bytes:
return bytes(a ^ b for a, b in zip(private_key, self._getXorKey(coin_type)))
@@ -1037,7 +1015,7 @@ class WalletManager:
_, data = segwit_addr.decode(self._getHRP(coin_type), address)
if data is None:
return ""
return hashlib.sha256(bytes([0x00, 0x14]) + bytes(data)).digest()[::-1].hex()
return sha256(bytes([0x00, 0x14]) + bytes(data))[::-1].hex()
def needsMigration(self, coin_type: Coins) -> bool:
cursor = self._swap_client.openDB()
@@ -1193,8 +1171,6 @@ class WalletManager:
self._swap_client.closeDB(cursor, commit=False)
def getSeedID(self, coin_type: Coins) -> Optional[str]:
from basicswap.contrib.test_framework.script import hash160
master_key = self._master_keys.get(coin_type)
if master_key is None:
return None
@@ -1204,16 +1180,12 @@ class WalletManager:
return hash160(ek.encode_p()).hex()
def signMessage(self, coin_type: Coins, address: str, message: str) -> bytes:
from coincurve import PrivateKey
key = self.getPrivateKey(coin_type, address)
if key is None:
raise ValueError(f"Cannot sign: no key for address {address}")
return PrivateKey(key).sign(message.encode("utf-8"))
def signHash(self, coin_type: Coins, address: str, msg_hash: bytes) -> bytes:
from coincurve import PrivateKey
key = self.getPrivateKey(coin_type, address)
if key is None:
raise ValueError(f"Cannot sign: no key for address {address}")
@@ -1222,8 +1194,6 @@ class WalletManager:
def getKeyForAddress(
self, coin_type: Coins, address: str
) -> Optional[Tuple[bytes, bytes]]:
from coincurve import PublicKey
key = self.getPrivateKey(coin_type, address)
if key is None:
return None
@@ -1232,8 +1202,6 @@ class WalletManager:
def findAddressByScripthash(
self, coin_type: Coins, scripthash: str
) -> Optional[str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -1256,8 +1224,6 @@ class WalletManager:
return None
def getAllScripthashes(self, coin_type: Coins) -> List[str]:
import sqlite3
try:
conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor()
@@ -1895,8 +1861,6 @@ class WalletManager:
for _ in existing:
return False
import json
pending = WalletPendingTx()
pending.coin_type = int(coin_type)
pending.txid = txid
@@ -1945,8 +1909,6 @@ class WalletManager:
) -> List[dict]:
cursor = self._swap_client.openDB()
try:
import json
results = self._swap_client.query(
WalletPendingTx,
cursor,
+9
View File
@@ -1,3 +1,12 @@
0.16.3
==============
- New setting "startup_delay"
- Adjusts the time waited for coin daemons to start between "startup_tries".
- Valid as a base setting and can be overridden per coin with chainclients settings.
0.14.5
==============
+3 -3
View File
@@ -135,15 +135,15 @@
(define-public basicswap
(package
(name "basicswap")
(version "0.16.0")
(version "0.16.2")
(source (origin
(method git-fetch)
(uri (git-reference
(url "https://github.com/basicswap/basicswap")
(commit "2c13314bdd29622235c92fd20c237801acb3cb76")))
(commit "3b76adeedbbf3586308b6bf8ba401cec899f8a61")))
(sha256
(base32
"0j0id6db3ljdsfag8krjdmd4rzlz2504yk9lzj0p89lqyygi9ilc"))
"0wvy1c6li45ivfnn93iry047fz7w088mcp5rbg07qnnbnb6lgqiz"))
(file-name (git-file-name name version))))
(build-system pyproject-build-system)
-1
View File
@@ -21,7 +21,6 @@ from basicswap.util import toBool
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
from basicswap.bin.prepare import downloadPIVXParams
TEST_HTTP_HOST = os.getenv(
"TEST_HTTP_HOST", "127.0.0.1"
) # Set to 0.0.0.0 when used in docker
+8 -12
View File
@@ -56,7 +56,6 @@ from tests.basicswap.extended.test_doge import (
import basicswap.config as cfg
import basicswap.bin.run as runSystem
TEST_PATH = os.path.expanduser(os.getenv("TEST_PATH", "~/test_basicswap1"))
PARTICL_PORT_BASE = int(os.getenv("PARTICL_PORT_BASE", BASE_PORT))
@@ -532,9 +531,7 @@ def run_prepare(
for opt in EXTRA_CONFIG_JSON.get("doge{}".format(node_id), []):
fp.write(opt + "\n")
with open(config_path) as fs:
settings = json.load(fs)
settings["startup_delay"] = 1
settings["min_delay_event"] = 1
settings["max_delay_event"] = 4
settings["min_delay_event_short"] = 1
@@ -586,7 +583,7 @@ def prepare_nodes(
class TestBase(unittest.TestCase):
def setUpClass(cls):
super(TestBase, cls).setUpClass()
super().setUpClass()
cls.delay_event = threading.Event()
signal.signal(
@@ -624,7 +621,7 @@ class TestBase(unittest.TestCase):
def run_process(client_id):
client_path = os.path.join(TEST_PATH, "client{}".format(client_id))
client_path = os.path.join(TEST_PATH, f"client{client_id}")
testargs = [
"basicswap-run",
"-datadir=" + client_path,
@@ -646,7 +643,7 @@ class XmrTestBase(TestBase):
prepare_nodes(3, "monero")
def start_processes(self):
multiprocessing.set_start_method("fork")
multiprocessing.set_start_method("spawn")
self.delay_event.clear()
for i in range(3):
@@ -655,7 +652,7 @@ class XmrTestBase(TestBase):
)
self.processes[-1].start()
waitForServer(self.delay_event, 12701)
waitForServer(self.delay_event, 12701, 60)
def waitForMainAddress():
for i in range(20):
@@ -667,13 +664,12 @@ class XmrTestBase(TestBase):
)
return wallets["XMR"]["main_address"]
except Exception as e:
print("Waiting for main address {}".format(str(e)))
print(f"Waiting for main address {e}")
self.delay_event.wait(1)
raise ValueError("waitForMainAddress timedout")
xmr_addr1 = waitForMainAddress()
num_blocks = 100
num_blocks: int = 100
xmr_auth = None
if os.getenv("XMR_RPC_USER", "") != "":
@@ -685,7 +681,7 @@ class XmrTestBase(TestBase):
]
< num_blocks
):
logging.info("Mining {} Monero blocks to {}.".format(num_blocks, xmr_addr1))
logging.info(f"Mining {num_blocks} Monero blocks to {xmr_addr1}.")
callrpc_xmr(
XMR_BASE_RPC_PORT + 1,
"generateblocks",
-1
View File
@@ -65,7 +65,6 @@ from tests.basicswap.common import (
)
from basicswap.bin.run import startDaemon
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
@@ -34,7 +34,6 @@ from tests.basicswap.util import (
read_json_api,
)
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
@@ -31,7 +31,6 @@ from tests.basicswap.util import (
waitForServer,
)
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
-1
View File
@@ -58,7 +58,6 @@ from tests.basicswap.common import (
from basicswap.bin.run import startDaemon
logger = logging.getLogger()
NUM_NODES = 3
-1
View File
@@ -40,7 +40,6 @@ from tests.basicswap.extended.test_dcr import (
run_test_itx_refund,
)
logger = logging.getLogger("BSX Tests")
if not len(logger.handlers):
+19 -3
View File
@@ -66,7 +66,6 @@ from tests.basicswap.common import (
from basicswap.bin.run import startDaemon
from basicswap.bin.prepare import downloadPIVXParams
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
@@ -182,6 +181,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"datadir": node_dir,
"bindir": cfg.PARTICL_BINDIR,
"blocks_confirmed": 2, # Faster testing
"wallet_name": "bsx_wallet",
},
"pivx": {
"connection_type": "rpc",
@@ -191,6 +191,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"bindir": PIVX_BINDIR,
"use_csv": False,
"use_segwit": False,
"wallet_name": "",
},
"bitcoin": {
"connection_type": "rpc",
@@ -199,6 +200,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"datadir": btcdatadir,
"bindir": cfg.BITCOIN_BINDIR,
"use_segwit": True,
"wallet_name": "bsx_wallet",
},
},
"check_progress_seconds": 2,
@@ -760,7 +762,17 @@ class Test(unittest.TestCase):
rtx = pivxRpc(f'getrawtransaction "{txid}" true')
assert rtx["version"] == 3
block_hash = pivxRpc(f'generatetoaddress 1 "{generate_addr}"')[0]
block_hash = None
for i in range(15):
rtx = pivxRpc(f'getrawtransaction "{txid}" true')
if "blockhash" in rtx:
block_hash = rtx["blockhash"]
logging.info(f"Shielded tx confirmed in block {block_hash} after {i}s")
break
if i == 5:
pivxRpc(f'generatetoaddress 1 "{generate_addr}"')
delay_event.wait(1)
assert block_hash is not None, "Shielded tx was not confirmed"
ci = self.swap_clients[0].ci(Coins.PIVX)
block = ci.getBlockWithTxns(block_hash)
@@ -837,7 +849,11 @@ class Test(unittest.TestCase):
swap_value = ci_from.make_int(swap_value)
assert swap_value > ci_from.make_int(9)
itx = pi.getFundedInitiateTxTemplate(ci_from, swap_value, True)
addr_to = pi.getMockAddrTo(ci_from)
funded_tx = ci_from.createRawFundedTransaction(
addr_to, swap_value, True, lock_unspents=True
)
itx = bytes.fromhex(funded_tx)
itx_decoded = ci_from.describeTx(itx.hex())
n = pi.findMockVout(ci_from, itx_decoded)
-1
View File
@@ -38,7 +38,6 @@ from tests.basicswap.util import (
waitForServer,
)
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
+27 -21
View File
@@ -248,7 +248,7 @@ def updateThreadDCR(cls):
if "double spend" in str(e):
pass
else:
logging.warning("updateThreadDCR purchaseticket {}".format(e))
logging.warning(f"updateThreadDCR purchaseticket {e}")
cls.delay_event.wait(0.5)
try:
if num_passed >= 5:
@@ -260,7 +260,7 @@ def updateThreadDCR(cls):
],
)
except Exception as e:
logging.warning("updateThreadDCR generate {}".format(e))
logging.warning(f"updateThreadDCR generate {e}")
except Exception as e:
print("updateThreadDCR error", str(e))
cls.delay_event.wait(random.uniform(cls.dcr_update_min, cls.dcr_update_max))
@@ -272,7 +272,7 @@ def signal_handler(self, sig, frame):
def run_process(client_id):
client_path = os.path.join(test_path, "client{}".format(client_id))
client_path = os.path.join(test_path, f"client{client_id}")
testargs = [
"basicswap-run",
"-datadir=" + client_path,
@@ -299,15 +299,24 @@ def start_processes(self):
for i in range(NUM_NODES):
waitForServer(self.delay_event, UI_PORT + i)
wallets = read_json_api(UI_PORT + 1, "wallets")
if "monero" in self.test_coins_list:
try:
for i in range(8):
wallets = read_json_api(UI_PORT + 1, "wallets")
if "XMR" in wallets and "main_address" in wallets["XMR"]:
break
logging.info("Waiting for wallets output")
self.delay_event.wait(1.0)
self.xmr_addr = wallets["XMR"]["main_address"]
except Exception as e:
logging.error("{} - wallets json: {}".format(str(e), json.dumps(wallets)))
raise
xmr_auth = None
if os.getenv("XMR_RPC_USER", "") != "":
xmr_auth = (os.getenv("XMR_RPC_USER", ""), os.getenv("XMR_RPC_PWD", ""))
self.xmr_addr = wallets["XMR"]["main_address"]
num_blocks = 100
num_blocks: int = 100
if (
callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[
"count"
@@ -322,10 +331,11 @@ def start_processes(self):
auth=xmr_auth,
)
logging.info(
"XMR blocks: %d",
"XMR blocks: {}".format(
callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[
"count"
],
]
)
)
self.btc_addr = callbtcrpc(0, "getnewaddress", ["mining_addr", "bech32"])
@@ -402,9 +412,7 @@ def start_processes(self):
have_blocks: int = callfirorpc(0, "getblockcount")
if have_blocks < num_blocks:
logging.info(
"Mining %d Firo blocks to %s",
num_blocks - have_blocks,
self.firo_addr,
f"Mining {num_blocks - have_blocks} Firo blocks to {self.firo_addr}"
)
callfirorpc(
0,
@@ -420,9 +428,7 @@ def start_processes(self):
have_blocks: int = callbchrpc(0, "getblockcount")
if have_blocks < num_blocks:
logging.info(
"Mining %d Bitcoincash blocks to %s",
num_blocks - have_blocks,
self.bch_addr,
f"Mining {num_blocks - have_blocks} Bitcoincash blocks to {self.bch_addr}"
)
callbchrpc(
0,
@@ -437,9 +443,7 @@ def start_processes(self):
have_blocks: int = calldogerpc(0, "getblockcount")
if have_blocks < num_blocks:
logging.info(
"Mining %d Dogecoin blocks to %s",
num_blocks - have_blocks,
self.doge_addr,
f"Mining {num_blocks - have_blocks} Dogecoin blocks to {self.doge_addr}"
)
calldogerpc(
0, "generatetoaddress", [num_blocks - have_blocks, self.doge_addr]
@@ -557,7 +561,10 @@ class BaseTestWithPrepare(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(BaseTestWithPrepare, cls).setUpClass()
cls.addClassCleanup(
cls.finalise
) # tearDownClass is not run if setUpClass fails
super().setUpClass()
random.seed(time.time())
@@ -577,7 +584,7 @@ class BaseTestWithPrepare(unittest.TestCase):
waitForServer(cls.delay_event, UI_PORT + 1)
@classmethod
def tearDownClass(cls):
def finalise(cls):
logging.info("Stopping test")
cls.delay_event.set()
if cls.update_thread:
@@ -598,7 +605,6 @@ class BaseTestWithPrepare(unittest.TestCase):
class Test(BaseTestWithPrepare):
def test_persistent(self):
while not self.delay_event.is_set():
logging.info("Looping indefinitely, ctrl+c to exit.")
self.delay_event.wait(10)
@@ -20,7 +20,6 @@ from util import (
)
from tests.basicswap.util import read_json_api
base_url = "http://localhost"
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022-2023 tecnovert
# Copyright (c) 2024-2025 The Basicswap developers
# Copyright (c) 2024-2026 The Basicswap developers
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
@@ -16,13 +16,31 @@ from tests.basicswap.util import (
from util import get_driver
from selenium.webdriver.common.by import By
logger = logging.getLogger()
logger.level = logging.INFO
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
def wait_for_balance(
port: int,
coin: str,
expect_amount: float,
balance_key: str = "balance",
iterations: int = 30,
delay_time: int = 1,
) -> None:
logger.info(f"Waiting for balance, port: {port}")
for i in range(iterations):
rv_js = read_json_api(port, f"wallets/{coin}")
if float(rv_js[balance_key]) >= expect_amount:
return
time.sleep(delay_time)
logger.warning(f"{port} wallets/{coin} {rv_js}")
raise ValueError(f"Expect {balance_key} {expect_amount}")
def clear_offers(port_list) -> None:
logger.info(f"clear_offers {port_list}")
@@ -60,7 +78,11 @@ def test_swap_dir(driver):
"automation_strat_id": 1,
}
rv = read_json_api(node_1_port, "offers/new", offer_data)
try:
offer_1_id = rv["offer_id"]
except Exception as e:
logger.info(f"rv: {rv}")
raise e
offer_data = {
"addr_from": -1,
@@ -72,7 +94,13 @@ def test_swap_dir(driver):
"automation_strat_id": 1,
}
rv = read_json_api(node_1_port, "offers/new", offer_data)
try:
offer_2_id = rv["offer_id"]
except Exception as e:
logger.info(f"rv: {rv}")
raise e
wait_for_balance(node_2_port, "xmr", 5.0)
offer_data = {
"addr_from": -1,
@@ -84,7 +112,11 @@ def test_swap_dir(driver):
"automation_strat_id": 1,
}
rv = read_json_api(node_2_port, "offers/new", offer_data)
try:
offer_3_id = rv["offer_id"]
except Exception as e:
logger.info(f"rv: {rv}")
raise e
# Wait for offers to propagate
for i in range(1000):
-1
View File
@@ -9,7 +9,6 @@
import os
from selenium.webdriver.common.by import By
BSX_0_PORT = int(os.getenv("BSX_0_PORT", 12701))
BSX_1_PORT = int(os.getenv("BSX_1_PORT", BSX_0_PORT + 1))
BSX_2_PORT = int(os.getenv("BSX_1_PORT", BSX_0_PORT + 2))
-1
View File
@@ -61,7 +61,6 @@ from basicswap.contrib.test_framework.messages import (
uint256_from_str,
)
logger = logging.getLogger()
-1
View File
@@ -63,7 +63,6 @@ from basicswap.contrib.test_framework.script import (
)
from tests.basicswap.test_xmr import BaseTest, test_delay_event, callnoderpc
logger = logging.getLogger()
-1
View File
@@ -91,7 +91,6 @@ from basicswap.db_util import (
)
from basicswap.bin.run import startDaemon, startXmrDaemon, startXmrWalletDaemon
logger = logging.getLogger()
NUM_NODES = 3
+2 -3
View File
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers
# Copyright (c) 2024-2026 The Basicswap developers
# Distributed under the MIT software license, see the accompanying
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
@@ -10,7 +10,6 @@ import json
import urllib
from urllib.request import urlopen
REQUIRED_SETTINGS = {
"blocks_confirmed": 1,
"conf_target": 1,
@@ -64,7 +63,7 @@ def waitForServer(delay_event, port, wait_for=20):
if delay_event.is_set():
raise ValueError("Test stopped.")
try:
delay_event.wait(1)
delay_event.wait(1.0)
_ = read_json_api(port)
return
except Exception as e: