mirror of
https://github.com/basicswap/basicswap.git
synced 2026-05-08 23:42:12 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be1dbaeeaa | |||
| 3b76adeedb | |||
| ae6691e7ab | |||
| 8482533b37 | |||
| 8fe0913fda | |||
| 9244a9fed8 | |||
| bfc58955da | |||
| f77b7dc363 | |||
| a6b5906a6d | |||
| 568eab1f31 | |||
| 1b86df9b60 | |||
| c8e7c02fe2 | |||
| 57a1a6505e |
@@ -101,6 +101,7 @@ jobs:
|
||||
cp -r $BIN_DIR/* ${TEST_PATH}/bin/
|
||||
pytest tests/basicswap/extended/test_encrypted_xmr_reload.py
|
||||
- name: Run selenium tests
|
||||
id: selenium_tests
|
||||
run: |
|
||||
export TEST_PATH=/tmp/test_persistent
|
||||
mkdir -p ${TEST_PATH}/bin
|
||||
@@ -126,3 +127,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,3 +1,3 @@
|
||||
name = "basicswap"
|
||||
|
||||
__version__ = "0.16.1"
|
||||
__version__ = "0.16.2"
|
||||
|
||||
+10
-16
@@ -939,32 +939,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 +1077,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)])
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -182,6 +182,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 +192,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 +201,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 +763,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 +850,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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -23,6 +23,25 @@ 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 +79,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 +95,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 +113,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):
|
||||
|
||||
Reference in New Issue
Block a user