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" 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" 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 - name: Run test_encrypted_xmr_reload
id: test_encrypted_xmr_reload
run: | run: |
export PYTHONPATH=$(pwd) export PYTHONPATH=$(pwd)
export TEST_PATH=${TEST_RELOAD_PATH} export TEST_PATH=${TEST_RELOAD_PATH}
mkdir -p ${TEST_PATH}/bin mkdir -p ${TEST_PATH}/bin
cp -r $BIN_DIR/* ${TEST_PATH}/bin/ cp -r $BIN_DIR/* ${TEST_PATH}/bin/
pytest tests/basicswap/extended/test_encrypted_xmr_reload.py 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 - name: Run selenium tests
id: selenium_tests
run: | run: |
export TEST_PATH=/tmp/test_persistent export TEST_PATH=/tmp/test_persistent
mkdir -p ${TEST_PATH}/bin mkdir -p ${TEST_PATH}/bin
@@ -126,3 +137,8 @@ jobs:
echo "Running test_swap_direction.py" echo "Running test_swap_direction.py"
python tests/basicswap/selenium/test_swap_direction.py python tests/basicswap/selenium/test_swap_direction.py
kill $TEST_NETWORK_PID 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" 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.log.warning(f"Setting mocktime to {new_offset}")
self.mock_time_offset = new_offset self.mock_time_offset = new_offset
def get_int_setting(self, name: str, default_v: int, min_v: int, max_v) -> int: def get_clamped_int_from(
value: int = self.settings.get(name, default_v) 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: if value < min_v:
self.log.warning(f"Setting {name} to {min_v}") self.log.warning(f"Setting {name} to {min_v}")
value = min_v value = min_v
@@ -375,6 +377,9 @@ class BaseApp(DBMethods):
value = max_v value = max_v
return value 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): def get_delay_event_seconds(self):
if self.min_delay_event == self.max_delay_event: if self.min_delay_event == self.max_delay_event:
return self.min_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.atomic_swap_1 as atomic_swap_1
import basicswap.protocols.xmr_swap_1 as xmr_swap_1 import basicswap.protocols.xmr_swap_1 as xmr_swap_1
PROTOCOL_VERSION_SECRET_HASH = 5 PROTOCOL_VERSION_SECRET_HASH = 5
MINPROTO_VERSION_SECRET_HASH = 4 MINPROTO_VERSION_SECRET_HASH = 4
@@ -441,9 +440,6 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
self.check_delayed_auto_accept_seconds = self.get_int_setting( self.check_delayed_auto_accept_seconds = self.get_int_setting(
"check_delayed_auto_accept_seconds", 60, 1, 20 * 60 "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_ui = self.settings.get("debug_ui", False)
self._debug_cases = [] self._debug_cases = []
self._last_checked_actions = 0 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 # systemd will try to restart the process if fail_code != 0
self.stopRunning(1) self.stopRunning(1)
startup_tries = self.startup_tries
chain_client_settings = self.getChainClientSettings(coin_type) chain_client_settings = self.getChainClientSettings(coin_type)
if "startup_tries" in chain_client_settings: # Total seconds waited for will be ((startup_tries(1 + startup_tries) / 2) * startup_delay
startup_tries = chain_client_settings["startup_tries"] startup_tries: int = self.get_clamped_int_from(
if startup_tries < 1: chain_client_settings,
self.log.warning('"startup_tries" can\'t be less than 1.') "startup_tries",
startup_tries = 1 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): for i in range(startup_tries):
if self.delay_event.is_set(): if self.delay_event.is_set():
return return
@@ -1632,6 +1637,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
self.coin_clients[coin_type]["interface"].testDaemonRPC(with_wallet) self.coin_clients[coin_type]["interface"].testDaemonRPC(with_wallet)
return return
except Exception as ex: except Exception as ex:
wait_for: int = startup_delay * (1 + i)
if any( if any(
log in str(ex) log in str(ex)
for log in [ for log in [
@@ -1644,13 +1650,13 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
] ]
): ):
self.log.info( 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: else:
self.log.warning( 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.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 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): def isValidSwapDest(self, ci, dest: bytes):
ensure(isinstance(dest, bytes), "Swap destination must be bytes") ensure(isinstance(dest, bytes), "Swap destination must be bytes")
if ci.coin_type() in (Coins.PART_BLIND,): if ci.coin_type() in (Coins.PART_BLIND,):
return ci.isValidPubkey(dest) return ci.verifyPubkey(dest)
# TODO: allow p2wsh # TODO: allow p2wsh
return ci.isValidAddressHash(dest) return ci.isValidAddressHash(dest)
@@ -10905,7 +10911,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
) )
ensure( ensure(
ci_from.isValidAddressHash(bid_data.dest_af) 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", "Invalid destination address",
) )
@@ -11896,7 +11902,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
) )
vkbs = ci_to.sumKeys(kbsl, kbsf) 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) address_to = self.getCachedMainWalletAddress(ci_to, cursor)
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON): elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
address_to = self.getCachedStealthAddressForCoin(coin_to, cursor) address_to = self.getCachedStealthAddressForCoin(coin_to, cursor)
@@ -13811,8 +13817,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
num_watched_outputs += len(v["watched_outputs"]) num_watched_outputs += len(v["watched_outputs"])
now: int = self.getTime() now: int = self.getTime()
q_bids_str: str = ( q_bids_str: str = """SELECT
"""SELECT
COUNT(CASE WHEN b.was_sent THEN 1 ELSE NULL END) AS count_sent, 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_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, 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 offers o ON b.offer_id = o.offer_id
JOIN bidstates s ON b.state = s.state_id JOIN bidstates s ON b.state = s.state_id
WHERE b.active_ind = 1""" WHERE b.active_ind = 1"""
)
q_offers_str: str = ( q_offers_str: str = """SELECT
"""SELECT
COUNT(CASE WHEN expire_at > :now THEN 1 ELSE NULL END) AS count_active, 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 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 COUNT(CASE WHEN was_sent AND expire_at > :now THEN 1 ELSE NULL END) AS count_sent_active
FROM offers WHERE active_ind = 1""" FROM offers WHERE active_ind = 1"""
)
try: try:
cursor = self.openDB() cursor = self.openDB()
-1
View File
@@ -12,7 +12,6 @@ import time
from enum import IntEnum, auto from enum import IntEnum, auto
from typing import Optional from typing import Optional
CURRENT_DB_VERSION = 34 CURRENT_DB_VERSION = 34
CURRENT_DB_DATA_VERSION = 8 CURRENT_DB_DATA_VERSION = 8
-1
View File
@@ -7,7 +7,6 @@
import json import json
default_coingecko_api_key = "CG-8hm3r9iLfpEXv4ied8oLbeUj" default_coingecko_api_key = "CG-8hm3r9iLfpEXv4ied8oLbeUj"
-7
View File
@@ -220,13 +220,6 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
if hash_len == 20: if hash_len == 20:
return True 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: def verifySig(self, pubkey: bytes, signed_hash: bytes, sig: bytes) -> bool:
pubkey = PublicKey(pubkey) pubkey = PublicKey(pubkey)
return pubkey.verify(sig, signed_hash, hasher=None) 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.chainparams import Coins
from basicswap.rpc import make_rpc_func, openrpc from basicswap.rpc import make_rpc_func, openrpc
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22 SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22
SEQUENCE_LOCKTIME_MASK = 0x0000FFFF SEQUENCE_LOCKTIME_MASK = 0x0000FFFF
@@ -939,32 +938,26 @@ class BTCInterface(Secp256k1Interface):
if wm: if wm:
info = wm.getAddressInfo(self.coin_type(), address) info = wm.getAddressInfo(self.coin_type(), address)
if info: if info:
if or_watch_only: if or_watch_only is False and info["is_watch_only"] is True:
return True return False
return True return True
return False return False
try: try:
addr_info = self.rpc_wallet("getaddressinfo", [address]) addr_info = self.rpc_wallet("getaddressinfo", [address])
if not or_watch_only:
if addr_info["ismine"]: if addr_info["ismine"]:
return True return True
else: if or_watch_only is False:
return False
if addr_info["iswatchonly"]:
return True
if self._use_descriptors: if self._use_descriptors:
addr_info = self.rpc_wallet_watch("getaddressinfo", [address]) wo_addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
if addr_info["ismine"] or addr_info["iswatchonly"]: if wo_addr_info["iswatchonly"]:
return True return True
except Exception as e: except Exception as e:
self._log.debug(f"isAddressMine RPC check failed: {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 return False
def checkAddressMine(self, address: str) -> None: def checkAddressMine(self, address: str) -> None:
@@ -1083,8 +1076,8 @@ class BTCInterface(Secp256k1Interface):
return self.encode_p2wsh(script) return self.encode_p2wsh(script)
def getDestForAddress(self, address: str) -> bytes: def getDestForAddress(self, address: str) -> bytes:
bech32_prefix = self.chainparams_network()["hrp"] bech32_prefix: str | None = self.chainparams_network().get("hrp", None)
if address.startswith(bech32_prefix + "1"): if bech32_prefix and address.startswith(bech32_prefix + "1"):
_, witprog = segwit_addr.decode(bech32_prefix, address) _, witprog = segwit_addr.decode(bech32_prefix, address)
return CScript([OP_0, bytes(witprog)]) return CScript([OP_0, bytes(witprog)])
-1
View File
@@ -82,7 +82,6 @@ from coincurve.ecdsaotves import (
ecdsaotves_rec_enc_key, ecdsaotves_rec_enc_key,
) )
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22 SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22
SEQUENCE_LOCKTIME_MASK = 0x0000F SEQUENCE_LOCKTIME_MASK = 0x0000F
+1 -1
View File
@@ -13,7 +13,7 @@ import subprocess
def createDCRWallet(args, hex_seed, logging, delay_event): def createDCRWallet(args, hex_seed, logging, delay_event):
logging.info("Creating DCR wallet") 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": if os.name == "nt":
str_args = " ".join(args) str_args = " ".join(args)
+1 -1
View File
@@ -361,7 +361,7 @@ class FIROInterface(BTCInterface):
) )
return pay_fee 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) key_wif = self.encodeKey(key)
rv = self.rpc( rv = self.rpc(
"signrawtransaction", "signrawtransaction",
+7 -23
View File
@@ -12,7 +12,7 @@ from .btc import BTCInterface
from basicswap.rpc import make_rpc_func from basicswap.rpc import make_rpc_func
from basicswap.chainparams import Coins from basicswap.chainparams import Coins
from basicswap.util.address import decodeAddress 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 ( from basicswap.contrib.test_framework.script import (
CScript, CScript,
OP_DUP, OP_DUP,
@@ -100,29 +100,13 @@ class PIVXInterface(BTCInterface):
return decodeAddress(address)[1:] return decodeAddress(address)[1:]
def getBlockWithTxns(self, block_hash): def getBlockWithTxns(self, block_hash):
# TODO: Bypass decoderawtransaction and getblockheader block = self.rpc("getblock", [block_hash, True])
block = self.rpc("getblock", [block_hash, False])
block_header = self.rpc("getblockheader", [block_hash])
decoded_block = CBlock()
decoded_block = FromHex(decoded_block, block)
tx_rv = [] tx_rv = []
for tx in decoded_block.vtx: for txid_str in block["tx"]:
tx_dec = self.rpc("decoderawtransaction", [ToHex(tx)]) tx_dec = self.rpc("getrawtransaction", [txid_str, True])
tx_rv.append(tx_dec) tx_rv.append(tx_dec)
block["tx"] = tx_rv
block_rv = { return block
"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
def withdrawCoin(self, value, addr_to, subfee): def withdrawCoin(self, value, addr_to, subfee):
params = [addr_to, value, "", "", subfee] params = [addr_to, value, "", "", subfee]
@@ -150,7 +134,7 @@ class PIVXInterface(BTCInterface):
) )
return pay_fee 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) key_wif = self.encodeKey(key)
rv = self.rpc( rv = self.rpc(
"signrawtransaction", "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.chainparams import XMR_COIN, Coins
from basicswap.interface.base import CoinInterface from basicswap.interface.base import CoinInterface
ed25519_l = 2**252 + 27742317777372353535851937790883648493 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_synced"] = sync_status.get("synced", False)
coin_entry["electrum_height"] = sync_status.get("height", 0) 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) coins_with_balances.append(coin_entry)
if k == Coins.PART: 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 from basicswap.util.integer import encode_varint, decode_varint
NPBW_INT = 0 NPBW_INT = 0
NPBW_BYTES = 2 NPBW_BYTES = 2
-1
View File
@@ -39,7 +39,6 @@ from basicswap.contrib.rfc6979 import (
rfc6979_hmac_sha256_generate, rfc6979_hmac_sha256_generate,
) )
START_TOKEN = 0xABCD START_TOKEN = 0xABCD
MSG_START_TOKEN = START_TOKEN.to_bytes(2, "big") 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? # TODO: Must be a better way?
logger.info("Initialising Simplex client") 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": if os.name == "nt":
str_args = " ".join(args) str_args = " ".join(args)
+8 -5
View File
@@ -351,16 +351,19 @@
); );
matchingCoins.forEach(coinData => { 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 => { balanceElements.forEach(element => {
const elementCoinName = element.getAttribute('data-coinname'); const elementCoinName = element.getAttribute('data-coinname');
if (elementCoinName === coinData.name) { 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 ticker = coinData.ticker || coinId.toUpperCase();
const newBalance = `${coinData.balance} ${ticker}`; const newBalance = balanceType === 'est_fee' ? value : `${value} ${ticker}`;
if (currentText !== newBalance) { if (element.textContent !== newBalance) {
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) { if (coinData.pending && parseFloat(coinData.pending) > 0) {
this.updatePendingBalance('Particl', 'Blind Balance:', coinData.pending, coinData.ticker || 'PART', 'Blind Unconfirmed:', coinData); 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 { } else {
this.updateSpecificBalance(coinData.name, 'Balance:', coinData.balance, coinData.ticker || coinData.name); 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.name !== 'Particl Anon' && coinData.name !== 'Particl Blind' && coinData.name !== 'Litecoin MWEB') {
if (coinData.pending && parseFloat(coinData.pending) > 0) { if (coinData.pending && parseFloat(coinData.pending) > 0) {
this.updatePendingDisplay(coinData); 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"> <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="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"> <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>) (<span class="usd-value"></span>)
{% if w.pending %} {% 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> <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"> <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="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"> <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>) (<span class="usd-value"></span>)
{% if w.blind_unconfirmed %} {% 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> <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"> <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="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"> <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>) (<span class="usd-value"></span>)
{% if w.anon_pending %} {% 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> <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 %} {% if is_electrum_mode %}
<span class="text-gray-400 dark:text-gray-300">Not available in light mode</span> <span class="text-gray-400 dark:text-gray-300">Not available in light mode</span>
{% else %} {% 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>) (<span class="usd-value"></span>)
{% if w.mweb_pending %} {% 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> <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"> <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="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"> <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>) (<span class="usd-value"></span>)
{% if w.spark_pending %} {% 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> <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"> <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="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"> <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>) (<span class="usd-value"></span>)
{% if w.spark_pending %} {% 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> <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> <td class="py-3 px-6">{{ w.expected_seed }}</td>
</tr> </tr>
{% endif %} {% 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> </table>
</div> </div>
</div> </div>
@@ -556,7 +547,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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-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"> <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>) (<span class="usd-value"></span>)
</td> </td>
</tr> </tr>
@@ -568,7 +559,7 @@
{% if is_electrum_mode %} {% if is_electrum_mode %}
<span class="text-gray-400 dark:text-gray-300">Not available in light mode</span> <span class="text-gray-400 dark:text-gray-300">Not available in light mode</span>
{% else %} {% 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>) (<span class="usd-value"></span>)
{% endif %} {% endif %}
</td> </td>
@@ -578,7 +569,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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-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"> <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>) (<span class="usd-value"></span>)
</td> </td>
</tr> </tr>
@@ -587,7 +578,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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-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"> <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>) (<span class="usd-value"></span>)
</td> </td>
</tr> </tr>
@@ -596,14 +587,14 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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-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"> <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>) (<span class="usd-value"></span>)
</td> </td>
</tr> </tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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-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"> <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>) (<span class="usd-value"></span>)
</td> </td>
</tr> </tr>
@@ -778,7 +769,7 @@
<tr class="opacity-100 text-gray-500 dark:text-gray-100"> <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 bold">Fee Estimate:</td>
<td class="py-3 px-6"> <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>) (<span class="usd-value fee-estimate-usd" data-decimals="8"></span>)
</td> </td>
</tr> </tr>
+5 -5
View File
@@ -65,7 +65,7 @@
<div class="p-6 bg-coolGray-100 dark:bg-gray-600"> <div class="p-6 bg-coolGray-100 dark:bg-gray-600">
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Balance:</h4> <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>
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white ">{{ w.ticker }} USD value:</h4> <h4 class="text-xs font-medium dark:text-white ">{{ w.ticker }} USD value:</h4>
@@ -90,7 +90,7 @@
{% if w.cid == '1' %} {# PART #} {% if w.cid == '1' %} {# PART #}
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Blind Balance:</h4> <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>
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Blind USD value:</h4> <h4 class="text-xs font-medium dark:text-white">Blind USD value:</h4>
@@ -108,7 +108,7 @@
{% endif %} {% endif %}
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Anon Balance:</h4> <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>
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Anon USD value:</h4> <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 #} {% 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"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">MWEB Balance:</h4> <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>
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">MWEB USD value:</h4> <h4 class="text-xs font-medium dark:text-white">MWEB USD value:</h4>
@@ -151,7 +151,7 @@
{% if w.cid == '13' %} {# FIRO #} {% if w.cid == '13' %} {# FIRO #}
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Spark Balance:</h4> <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>
<div class="flex mb-2 justify-between items-center"> <div class="flex mb-2 justify-between items-center">
<h4 class="text-xs font-medium dark:text-white">Spark USD value:</h4> <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 time
import decimal import decimal
COIN = 100000000 COIN = 100000000
-1
View File
@@ -25,7 +25,6 @@ from basicswap.contrib.test_framework.messages import (
uint256_from_str, uint256_from_str,
) )
AES_BLOCK_SIZE = 16 AES_BLOCK_SIZE = 16
+9 -47
View File
@@ -4,10 +4,12 @@
# 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 hashlib import json
import sqlite3
import threading import threading
import time import time
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from coincurve import PrivateKey, PublicKey
from .chainparams import Coins from .chainparams import Coins
from .contrib.test_framework import segwit_addr from .contrib.test_framework import segwit_addr
@@ -19,7 +21,7 @@ from .db_wallet import (
WalletTxCache, WalletTxCache,
WalletWatchOnly, WalletWatchOnly,
) )
from .util.crypto import hash160 from .util.crypto import hash160, sha256
from .util.extkey import ExtKeyPair from .util.extkey import ExtKeyPair
@@ -112,13 +114,11 @@ class WalletManager:
def _deriveAddress( def _deriveAddress(
self, coin_type: Coins, index: int, internal: bool = False self, coin_type: Coins, index: int, internal: bool = False
) -> Tuple[str, str, bytes]: ) -> Tuple[str, str, bytes]:
from coincurve import PublicKey
key = self._deriveKey(coin_type, index, internal) key = self._deriveKey(coin_type, index, internal)
pubkey = PublicKey.from_secret(key).format() pubkey = PublicKey.from_secret(key).format()
pkh = hash160(pubkey) pkh = hash160(pubkey)
address = segwit_addr.encode(self._getHRP(coin_type), 0, pkh) 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 return address, scripthash, pubkey
def _syncStateIndices(self, coin_type: Coins, cursor) -> None: def _syncStateIndices(self, coin_type: Coins, cursor) -> None:
@@ -275,8 +275,6 @@ class WalletManager:
def getAddress( def getAddress(
self, coin_type: Coins, index: int, internal: bool = False self, coin_type: Coins, index: int, internal: bool = False
) -> Optional[str]: ) -> Optional[str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -395,8 +393,6 @@ class WalletManager:
include_watch_only: bool = True, include_watch_only: bool = True,
funded_only: bool = False, funded_only: bool = False,
) -> List[str]: ) -> List[str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -426,8 +422,6 @@ class WalletManager:
return [] return []
def getFundedAddresses(self, coin_type: Coins) -> Dict[str, str]: def getFundedAddresses(self, coin_type: Coins) -> Dict[str, str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -452,8 +446,6 @@ class WalletManager:
return {} return {}
def getExistingInternalAddress(self, coin_type: Coins) -> Optional[str]: def getExistingInternalAddress(self, coin_type: Coins) -> Optional[str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -517,8 +509,6 @@ class WalletManager:
self._swap_client.closeDB(cursor, commit=False) self._swap_client.closeDB(cursor, commit=False)
def getAddressInfo(self, coin_type: Coins, address: str) -> Optional[dict]: def getAddressInfo(self, coin_type: Coins, address: str) -> Optional[dict]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -559,8 +549,6 @@ class WalletManager:
return None return None
def getCachedTotalBalance(self, coin_type: Coins) -> int: def getCachedTotalBalance(self, coin_type: Coins) -> int:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -700,8 +688,6 @@ class WalletManager:
if not self.isInitialized(coin_type): if not self.isInitialized(coin_type):
return None return None
import sqlite3
now = int(time.time()) now = int(time.time())
min_cache_time = now - max_cache_age min_cache_time = now - max_cache_age
@@ -744,8 +730,6 @@ class WalletManager:
return None return None
def hasCachedBalances(self, coin_type: Coins, max_cache_age: int = 120) -> bool: def hasCachedBalances(self, coin_type: Coins, max_cache_age: int = 120) -> bool:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -762,8 +746,6 @@ class WalletManager:
def getPrivateKey(self, coin_type: Coins, address: str) -> Optional[bytes]: def getPrivateKey(self, coin_type: Coins, address: str) -> Optional[bytes]:
if not self.isInitialized(coin_type): if not self.isInitialized(coin_type):
return None return None
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -788,8 +770,6 @@ class WalletManager:
return None return None
def getSignableAddresses(self, coin_type: Coins) -> Dict[str, str]: def getSignableAddresses(self, coin_type: Coins) -> Dict[str, str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -861,10 +841,8 @@ class WalletManager:
label: str = "", label: str = "",
source: str = "import", source: str = "import",
) -> bool: ) -> bool:
from coincurve import PublicKey as CCPublicKey
try: try:
pubkey = CCPublicKey.from_secret(private_key).format() pubkey = PublicKey.from_secret(private_key).format()
if ( if (
segwit_addr.encode(self._getHRP(coin_type), 0, hash160(pubkey)) segwit_addr.encode(self._getHRP(coin_type), 0, hash160(pubkey))
!= address != address
@@ -1003,7 +981,7 @@ class WalletManager:
def _b58decode_check(self, s: str) -> bytes: def _b58decode_check(self, s: str) -> bytes:
data = self._b58decode(s) data = self._b58decode(s)
payload, checksum = data[:-4], data[-4:] payload, checksum = data[:-4], data[-4:]
expected = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] expected = sha256(sha256(payload))[:4]
if checksum != expected: if checksum != expected:
raise ValueError("Invalid base58 checksum") raise ValueError("Invalid base58 checksum")
return payload return payload
@@ -1025,7 +1003,7 @@ class WalletManager:
master_key = self._master_keys.get(coin_type) master_key = self._master_keys.get(coin_type)
if master_key is None: if master_key is None:
raise ValueError(f"Wallet not initialized for {coin_type}") 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: 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))) 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) _, data = segwit_addr.decode(self._getHRP(coin_type), address)
if data is None: if data is None:
return "" 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: def needsMigration(self, coin_type: Coins) -> bool:
cursor = self._swap_client.openDB() cursor = self._swap_client.openDB()
@@ -1193,8 +1171,6 @@ class WalletManager:
self._swap_client.closeDB(cursor, commit=False) self._swap_client.closeDB(cursor, commit=False)
def getSeedID(self, coin_type: Coins) -> Optional[str]: def getSeedID(self, coin_type: Coins) -> Optional[str]:
from basicswap.contrib.test_framework.script import hash160
master_key = self._master_keys.get(coin_type) master_key = self._master_keys.get(coin_type)
if master_key is None: if master_key is None:
return None return None
@@ -1204,16 +1180,12 @@ class WalletManager:
return hash160(ek.encode_p()).hex() return hash160(ek.encode_p()).hex()
def signMessage(self, coin_type: Coins, address: str, message: str) -> bytes: def signMessage(self, coin_type: Coins, address: str, message: str) -> bytes:
from coincurve import PrivateKey
key = self.getPrivateKey(coin_type, address) key = self.getPrivateKey(coin_type, address)
if key is None: if key is None:
raise ValueError(f"Cannot sign: no key for address {address}") raise ValueError(f"Cannot sign: no key for address {address}")
return PrivateKey(key).sign(message.encode("utf-8")) return PrivateKey(key).sign(message.encode("utf-8"))
def signHash(self, coin_type: Coins, address: str, msg_hash: bytes) -> bytes: def signHash(self, coin_type: Coins, address: str, msg_hash: bytes) -> bytes:
from coincurve import PrivateKey
key = self.getPrivateKey(coin_type, address) key = self.getPrivateKey(coin_type, address)
if key is None: if key is None:
raise ValueError(f"Cannot sign: no key for address {address}") raise ValueError(f"Cannot sign: no key for address {address}")
@@ -1222,8 +1194,6 @@ class WalletManager:
def getKeyForAddress( def getKeyForAddress(
self, coin_type: Coins, address: str self, coin_type: Coins, address: str
) -> Optional[Tuple[bytes, bytes]]: ) -> Optional[Tuple[bytes, bytes]]:
from coincurve import PublicKey
key = self.getPrivateKey(coin_type, address) key = self.getPrivateKey(coin_type, address)
if key is None: if key is None:
return None return None
@@ -1232,8 +1202,6 @@ class WalletManager:
def findAddressByScripthash( def findAddressByScripthash(
self, coin_type: Coins, scripthash: str self, coin_type: Coins, scripthash: str
) -> Optional[str]: ) -> Optional[str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -1256,8 +1224,6 @@ class WalletManager:
return None return None
def getAllScripthashes(self, coin_type: Coins) -> List[str]: def getAllScripthashes(self, coin_type: Coins) -> List[str]:
import sqlite3
try: try:
conn = sqlite3.connect(self._swap_client.sqlite_file) conn = sqlite3.connect(self._swap_client.sqlite_file)
cursor = conn.cursor() cursor = conn.cursor()
@@ -1895,8 +1861,6 @@ class WalletManager:
for _ in existing: for _ in existing:
return False return False
import json
pending = WalletPendingTx() pending = WalletPendingTx()
pending.coin_type = int(coin_type) pending.coin_type = int(coin_type)
pending.txid = txid pending.txid = txid
@@ -1945,8 +1909,6 @@ class WalletManager:
) -> List[dict]: ) -> List[dict]:
cursor = self._swap_client.openDB() cursor = self._swap_client.openDB()
try: try:
import json
results = self._swap_client.query( results = self._swap_client.query(
WalletPendingTx, WalletPendingTx,
cursor, 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 0.14.5
============== ==============
+3 -3
View File
@@ -135,15 +135,15 @@
(define-public basicswap (define-public basicswap
(package (package
(name "basicswap") (name "basicswap")
(version "0.16.0") (version "0.16.2")
(source (origin (source (origin
(method git-fetch) (method git-fetch)
(uri (git-reference (uri (git-reference
(url "https://github.com/basicswap/basicswap") (url "https://github.com/basicswap/basicswap")
(commit "2c13314bdd29622235c92fd20c237801acb3cb76"))) (commit "3b76adeedbbf3586308b6bf8ba401cec899f8a61")))
(sha256 (sha256
(base32 (base32
"0j0id6db3ljdsfag8krjdmd4rzlz2504yk9lzj0p89lqyygi9ilc")) "0wvy1c6li45ivfnn93iry047fz7w088mcp5rbg07qnnbnb6lgqiz"))
(file-name (git-file-name name version)))) (file-name (git-file-name name version))))
(build-system pyproject-build-system) (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.contrib.rpcauth import generate_salt, password_to_hmac
from basicswap.bin.prepare import downloadPIVXParams from basicswap.bin.prepare import downloadPIVXParams
TEST_HTTP_HOST = os.getenv( TEST_HTTP_HOST = os.getenv(
"TEST_HTTP_HOST", "127.0.0.1" "TEST_HTTP_HOST", "127.0.0.1"
) # Set to 0.0.0.0 when used in docker ) # 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.config as cfg
import basicswap.bin.run as runSystem import basicswap.bin.run as runSystem
TEST_PATH = os.path.expanduser(os.getenv("TEST_PATH", "~/test_basicswap1")) TEST_PATH = os.path.expanduser(os.getenv("TEST_PATH", "~/test_basicswap1"))
PARTICL_PORT_BASE = int(os.getenv("PARTICL_PORT_BASE", BASE_PORT)) 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), []): for opt in EXTRA_CONFIG_JSON.get("doge{}".format(node_id), []):
fp.write(opt + "\n") fp.write(opt + "\n")
with open(config_path) as fs: settings["startup_delay"] = 1
settings = json.load(fs)
settings["min_delay_event"] = 1 settings["min_delay_event"] = 1
settings["max_delay_event"] = 4 settings["max_delay_event"] = 4
settings["min_delay_event_short"] = 1 settings["min_delay_event_short"] = 1
@@ -586,7 +583,7 @@ def prepare_nodes(
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
def setUpClass(cls): def setUpClass(cls):
super(TestBase, cls).setUpClass() super().setUpClass()
cls.delay_event = threading.Event() cls.delay_event = threading.Event()
signal.signal( signal.signal(
@@ -624,7 +621,7 @@ class TestBase(unittest.TestCase):
def run_process(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, f"client{client_id}")
testargs = [ testargs = [
"basicswap-run", "basicswap-run",
"-datadir=" + client_path, "-datadir=" + client_path,
@@ -646,7 +643,7 @@ class XmrTestBase(TestBase):
prepare_nodes(3, "monero") prepare_nodes(3, "monero")
def start_processes(self): def start_processes(self):
multiprocessing.set_start_method("fork") multiprocessing.set_start_method("spawn")
self.delay_event.clear() self.delay_event.clear()
for i in range(3): for i in range(3):
@@ -655,7 +652,7 @@ class XmrTestBase(TestBase):
) )
self.processes[-1].start() self.processes[-1].start()
waitForServer(self.delay_event, 12701) waitForServer(self.delay_event, 12701, 60)
def waitForMainAddress(): def waitForMainAddress():
for i in range(20): for i in range(20):
@@ -667,13 +664,12 @@ class XmrTestBase(TestBase):
) )
return wallets["XMR"]["main_address"] return wallets["XMR"]["main_address"]
except Exception as e: except Exception as e:
print("Waiting for main address {}".format(str(e))) print(f"Waiting for main address {e}")
self.delay_event.wait(1) self.delay_event.wait(1)
raise ValueError("waitForMainAddress timedout") raise ValueError("waitForMainAddress timedout")
xmr_addr1 = waitForMainAddress() xmr_addr1 = waitForMainAddress()
num_blocks: int = 100
num_blocks = 100
xmr_auth = None xmr_auth = None
if os.getenv("XMR_RPC_USER", "") != "": if os.getenv("XMR_RPC_USER", "") != "":
@@ -685,7 +681,7 @@ class XmrTestBase(TestBase):
] ]
< num_blocks < 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( callrpc_xmr(
XMR_BASE_RPC_PORT + 1, XMR_BASE_RPC_PORT + 1,
"generateblocks", "generateblocks",
-1
View File
@@ -65,7 +65,6 @@ from tests.basicswap.common import (
) )
from basicswap.bin.run import startDaemon from basicswap.bin.run import startDaemon
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
@@ -34,7 +34,6 @@ from tests.basicswap.util import (
read_json_api, read_json_api,
) )
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
@@ -31,7 +31,6 @@ from tests.basicswap.util import (
waitForServer, waitForServer,
) )
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
-1
View File
@@ -58,7 +58,6 @@ from tests.basicswap.common import (
from basicswap.bin.run import startDaemon from basicswap.bin.run import startDaemon
logger = logging.getLogger() logger = logging.getLogger()
NUM_NODES = 3 NUM_NODES = 3
-1
View File
@@ -40,7 +40,6 @@ from tests.basicswap.extended.test_dcr import (
run_test_itx_refund, run_test_itx_refund,
) )
logger = logging.getLogger("BSX Tests") logger = logging.getLogger("BSX Tests")
if not len(logger.handlers): 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.run import startDaemon
from basicswap.bin.prepare import downloadPIVXParams from basicswap.bin.prepare import downloadPIVXParams
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
@@ -182,6 +181,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"datadir": node_dir, "datadir": node_dir,
"bindir": cfg.PARTICL_BINDIR, "bindir": cfg.PARTICL_BINDIR,
"blocks_confirmed": 2, # Faster testing "blocks_confirmed": 2, # Faster testing
"wallet_name": "bsx_wallet",
}, },
"pivx": { "pivx": {
"connection_type": "rpc", "connection_type": "rpc",
@@ -191,6 +191,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"bindir": PIVX_BINDIR, "bindir": PIVX_BINDIR,
"use_csv": False, "use_csv": False,
"use_segwit": False, "use_segwit": False,
"wallet_name": "",
}, },
"bitcoin": { "bitcoin": {
"connection_type": "rpc", "connection_type": "rpc",
@@ -199,6 +200,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
"datadir": btcdatadir, "datadir": btcdatadir,
"bindir": cfg.BITCOIN_BINDIR, "bindir": cfg.BITCOIN_BINDIR,
"use_segwit": True, "use_segwit": True,
"wallet_name": "bsx_wallet",
}, },
}, },
"check_progress_seconds": 2, "check_progress_seconds": 2,
@@ -760,7 +762,17 @@ class Test(unittest.TestCase):
rtx = pivxRpc(f'getrawtransaction "{txid}" true') rtx = pivxRpc(f'getrawtransaction "{txid}" true')
assert rtx["version"] == 3 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) ci = self.swap_clients[0].ci(Coins.PIVX)
block = ci.getBlockWithTxns(block_hash) block = ci.getBlockWithTxns(block_hash)
@@ -837,7 +849,11 @@ class Test(unittest.TestCase):
swap_value = ci_from.make_int(swap_value) swap_value = ci_from.make_int(swap_value)
assert swap_value > ci_from.make_int(9) 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()) itx_decoded = ci_from.describeTx(itx.hex())
n = pi.findMockVout(ci_from, itx_decoded) n = pi.findMockVout(ci_from, itx_decoded)
-1
View File
@@ -38,7 +38,6 @@ from tests.basicswap.util import (
waitForServer, waitForServer,
) )
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
+27 -21
View File
@@ -248,7 +248,7 @@ def updateThreadDCR(cls):
if "double spend" in str(e): if "double spend" in str(e):
pass pass
else: else:
logging.warning("updateThreadDCR purchaseticket {}".format(e)) logging.warning(f"updateThreadDCR purchaseticket {e}")
cls.delay_event.wait(0.5) cls.delay_event.wait(0.5)
try: try:
if num_passed >= 5: if num_passed >= 5:
@@ -260,7 +260,7 @@ def updateThreadDCR(cls):
], ],
) )
except Exception as e: except Exception as e:
logging.warning("updateThreadDCR generate {}".format(e)) logging.warning(f"updateThreadDCR generate {e}")
except Exception as e: except Exception as e:
print("updateThreadDCR error", str(e)) print("updateThreadDCR error", str(e))
cls.delay_event.wait(random.uniform(cls.dcr_update_min, cls.dcr_update_max)) 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): 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 = [ testargs = [
"basicswap-run", "basicswap-run",
"-datadir=" + client_path, "-datadir=" + client_path,
@@ -299,15 +299,24 @@ def start_processes(self):
for i in range(NUM_NODES): for i in range(NUM_NODES):
waitForServer(self.delay_event, UI_PORT + i) waitForServer(self.delay_event, UI_PORT + i)
wallets = read_json_api(UI_PORT + 1, "wallets")
if "monero" in self.test_coins_list: 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 xmr_auth = None
if os.getenv("XMR_RPC_USER", "") != "": if os.getenv("XMR_RPC_USER", "") != "":
xmr_auth = (os.getenv("XMR_RPC_USER", ""), os.getenv("XMR_RPC_PWD", "")) xmr_auth = (os.getenv("XMR_RPC_USER", ""), os.getenv("XMR_RPC_PWD", ""))
self.xmr_addr = wallets["XMR"]["main_address"] num_blocks: int = 100
num_blocks = 100
if ( if (
callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[ callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[
"count" "count"
@@ -322,10 +331,11 @@ def start_processes(self):
auth=xmr_auth, auth=xmr_auth,
) )
logging.info( logging.info(
"XMR blocks: %d", "XMR blocks: {}".format(
callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[ callrpc_xmr(XMR_BASE_RPC_PORT + 1, "get_block_count", auth=xmr_auth)[
"count" "count"
], ]
)
) )
self.btc_addr = callbtcrpc(0, "getnewaddress", ["mining_addr", "bech32"]) self.btc_addr = callbtcrpc(0, "getnewaddress", ["mining_addr", "bech32"])
@@ -402,9 +412,7 @@ def start_processes(self):
have_blocks: int = callfirorpc(0, "getblockcount") have_blocks: int = callfirorpc(0, "getblockcount")
if have_blocks < num_blocks: if have_blocks < num_blocks:
logging.info( logging.info(
"Mining %d Firo blocks to %s", f"Mining {num_blocks - have_blocks} Firo blocks to {self.firo_addr}"
num_blocks - have_blocks,
self.firo_addr,
) )
callfirorpc( callfirorpc(
0, 0,
@@ -420,9 +428,7 @@ def start_processes(self):
have_blocks: int = callbchrpc(0, "getblockcount") have_blocks: int = callbchrpc(0, "getblockcount")
if have_blocks < num_blocks: if have_blocks < num_blocks:
logging.info( logging.info(
"Mining %d Bitcoincash blocks to %s", f"Mining {num_blocks - have_blocks} Bitcoincash blocks to {self.bch_addr}"
num_blocks - have_blocks,
self.bch_addr,
) )
callbchrpc( callbchrpc(
0, 0,
@@ -437,9 +443,7 @@ def start_processes(self):
have_blocks: int = calldogerpc(0, "getblockcount") have_blocks: int = calldogerpc(0, "getblockcount")
if have_blocks < num_blocks: if have_blocks < num_blocks:
logging.info( logging.info(
"Mining %d Dogecoin blocks to %s", f"Mining {num_blocks - have_blocks} Dogecoin blocks to {self.doge_addr}"
num_blocks - have_blocks,
self.doge_addr,
) )
calldogerpc( calldogerpc(
0, "generatetoaddress", [num_blocks - have_blocks, self.doge_addr] 0, "generatetoaddress", [num_blocks - have_blocks, self.doge_addr]
@@ -557,7 +561,10 @@ class BaseTestWithPrepare(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(BaseTestWithPrepare, cls).setUpClass() cls.addClassCleanup(
cls.finalise
) # tearDownClass is not run if setUpClass fails
super().setUpClass()
random.seed(time.time()) random.seed(time.time())
@@ -577,7 +584,7 @@ class BaseTestWithPrepare(unittest.TestCase):
waitForServer(cls.delay_event, UI_PORT + 1) waitForServer(cls.delay_event, UI_PORT + 1)
@classmethod @classmethod
def tearDownClass(cls): def finalise(cls):
logging.info("Stopping test") logging.info("Stopping test")
cls.delay_event.set() cls.delay_event.set()
if cls.update_thread: if cls.update_thread:
@@ -598,7 +605,6 @@ class BaseTestWithPrepare(unittest.TestCase):
class Test(BaseTestWithPrepare): class Test(BaseTestWithPrepare):
def test_persistent(self): def test_persistent(self):
while not self.delay_event.is_set(): while not self.delay_event.is_set():
logging.info("Looping indefinitely, ctrl+c to exit.") logging.info("Looping indefinitely, ctrl+c to exit.")
self.delay_event.wait(10) self.delay_event.wait(10)
@@ -20,7 +20,6 @@ from util import (
) )
from tests.basicswap.util import read_json_api from tests.basicswap.util import read_json_api
base_url = "http://localhost" base_url = "http://localhost"
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022-2023 tecnovert # 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 # 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.
@@ -16,13 +16,31 @@ from tests.basicswap.util import (
from util import get_driver from util import get_driver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.INFO logger.level = logging.INFO
if not len(logger.handlers): if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout)) 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: def clear_offers(port_list) -> None:
logger.info(f"clear_offers {port_list}") logger.info(f"clear_offers {port_list}")
@@ -60,7 +78,11 @@ def test_swap_dir(driver):
"automation_strat_id": 1, "automation_strat_id": 1,
} }
rv = read_json_api(node_1_port, "offers/new", offer_data) rv = read_json_api(node_1_port, "offers/new", offer_data)
try:
offer_1_id = rv["offer_id"] offer_1_id = rv["offer_id"]
except Exception as e:
logger.info(f"rv: {rv}")
raise e
offer_data = { offer_data = {
"addr_from": -1, "addr_from": -1,
@@ -72,7 +94,13 @@ def test_swap_dir(driver):
"automation_strat_id": 1, "automation_strat_id": 1,
} }
rv = read_json_api(node_1_port, "offers/new", offer_data) rv = read_json_api(node_1_port, "offers/new", offer_data)
try:
offer_2_id = rv["offer_id"] 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 = { offer_data = {
"addr_from": -1, "addr_from": -1,
@@ -84,7 +112,11 @@ def test_swap_dir(driver):
"automation_strat_id": 1, "automation_strat_id": 1,
} }
rv = read_json_api(node_2_port, "offers/new", offer_data) rv = read_json_api(node_2_port, "offers/new", offer_data)
try:
offer_3_id = rv["offer_id"] offer_3_id = rv["offer_id"]
except Exception as e:
logger.info(f"rv: {rv}")
raise e
# Wait for offers to propagate # Wait for offers to propagate
for i in range(1000): for i in range(1000):
-1
View File
@@ -9,7 +9,6 @@
import os import os
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
BSX_0_PORT = int(os.getenv("BSX_0_PORT", 12701)) BSX_0_PORT = int(os.getenv("BSX_0_PORT", 12701))
BSX_1_PORT = int(os.getenv("BSX_1_PORT", BSX_0_PORT + 1)) 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)) 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, uint256_from_str,
) )
logger = logging.getLogger() 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 from tests.basicswap.test_xmr import BaseTest, test_delay_event, callnoderpc
logger = logging.getLogger() logger = logging.getLogger()
-1
View File
@@ -91,7 +91,6 @@ from basicswap.db_util import (
) )
from basicswap.bin.run import startDaemon, startXmrDaemon, startXmrWalletDaemon from basicswap.bin.run import startDaemon, startXmrDaemon, startXmrWalletDaemon
logger = logging.getLogger() logger = logging.getLogger()
NUM_NODES = 3 NUM_NODES = 3
+2 -3
View File
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022-2024 tecnovert # 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 # Distributed under the MIT software license, see the accompanying
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php. # file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
@@ -10,7 +10,6 @@ import json
import urllib import urllib
from urllib.request import urlopen from urllib.request import urlopen
REQUIRED_SETTINGS = { REQUIRED_SETTINGS = {
"blocks_confirmed": 1, "blocks_confirmed": 1,
"conf_target": 1, "conf_target": 1,
@@ -64,7 +63,7 @@ def waitForServer(delay_event, port, wait_for=20):
if delay_event.is_set(): if delay_event.is_set():
raise ValueError("Test stopped.") raise ValueError("Test stopped.")
try: try:
delay_event.wait(1) delay_event.wait(1.0)
_ = read_json_api(port) _ = read_json_api(port)
return return
except Exception as e: except Exception as e: