mirror of
https://github.com/basicswap/basicswap.git
synced 2026-06-08 04:01:41 +02:00
Merge pull request #489 from tecnovert/csv_check_fix
fix: get refund tx block info for CSV check
This commit is contained in:
@@ -92,7 +92,7 @@ jobs:
|
|||||||
export PARTICL_BINDIR="$BIN_DIR/particl"
|
export PARTICL_BINDIR="$BIN_DIR/particl"
|
||||||
export BITCOIN_BINDIR="$BIN_DIR/bitcoin"
|
export BITCOIN_BINDIR="$BIN_DIR/bitcoin"
|
||||||
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 or test_11_fee_validation"
|
pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx or test_03_a_follower_recover_a_lock_tx or test_11_fee_validation"
|
||||||
- name: Run test_encrypted_xmr_reload
|
- name: Run test_encrypted_xmr_reload
|
||||||
id: test_encrypted_xmr_reload
|
id: test_encrypted_xmr_reload
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+70
-19
@@ -5552,8 +5552,8 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
use_cursor = self.openDB(cursor)
|
use_cursor = self.openDB(cursor)
|
||||||
|
|
||||||
bid, offer = self.getBidAndOffer(bid_id, use_cursor)
|
bid, offer = self.getBidAndOffer(bid_id, use_cursor)
|
||||||
ensure(bid, "Bid not found")
|
ensure(bid, f"Bid not found: {self.log.id(bid_id)}.")
|
||||||
ensure(offer, "Offer not found")
|
ensure(offer, f"Offer not found: {self.log.id(bid.offer_id)}.")
|
||||||
|
|
||||||
# Ensure bid is still valid
|
# Ensure bid is still valid
|
||||||
now: int = self.getTime()
|
now: int = self.getTime()
|
||||||
@@ -6828,8 +6828,8 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
try:
|
try:
|
||||||
use_cursor = self.openDB(cursor)
|
use_cursor = self.openDB(cursor)
|
||||||
bid, offer = self.getBidAndOffer(bid_id, use_cursor, with_txns=False)
|
bid, offer = self.getBidAndOffer(bid_id, use_cursor, with_txns=False)
|
||||||
ensure(bid, "Bid not found")
|
ensure(bid, f"Bid not found: {self.log.id(bid_id)}.")
|
||||||
ensure(offer, "Offer not found")
|
ensure(offer, f"Offer not found: {self.log.id(bid.offer_id)}.")
|
||||||
|
|
||||||
bid.setState(new_state)
|
bid.setState(new_state)
|
||||||
self.deactivateBid(use_cursor, offer, bid)
|
self.deactivateBid(use_cursor, offer, bid)
|
||||||
@@ -7747,7 +7747,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id,
|
bid.bid_id,
|
||||||
EventLogTypes.DEBUG_TWEAK_APPLIED,
|
EventLogTypes.DEBUG_TWEAK_APPLIED,
|
||||||
"ind {}".format(bid.debug_ind),
|
f"ind {bid.debug_ind}",
|
||||||
cursor,
|
cursor,
|
||||||
)
|
)
|
||||||
self.commitDB()
|
self.commitDB()
|
||||||
@@ -7802,6 +7802,23 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
||||||
self.commitDB()
|
self.commitDB()
|
||||||
|
|
||||||
|
if refund_tx.block_height is None:
|
||||||
|
self.log.debug(
|
||||||
|
f"A_LOCK_REFUND tx: {self.logIDT(refund_tx.txid)} block height not known, bid: {self.log.id(bid_id)}"
|
||||||
|
)
|
||||||
|
refund_tx_info = ci_from.getTxOutInfo(
|
||||||
|
refund_tx.txid, refund_tx.vout
|
||||||
|
)
|
||||||
|
if refund_tx_info:
|
||||||
|
refund_tx.block_hash = refund_tx_info["block_hash"]
|
||||||
|
refund_tx.block_height = refund_tx_info["block_height"]
|
||||||
|
refund_tx.block_time = refund_tx_info["block_time"]
|
||||||
|
self.log.debug(
|
||||||
|
f"Found A_LOCK_REFUND tx block height: {refund_tx.block_height}, time: {refund_tx.block_time}"
|
||||||
|
)
|
||||||
|
self.add(refund_tx, cursor, upsert=True)
|
||||||
|
self.commitDB()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns
|
TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE not in bid.txns
|
||||||
and refund_tx.block_height is not None
|
and refund_tx.block_height is not None
|
||||||
@@ -7947,10 +7964,14 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
"",
|
"",
|
||||||
cursor,
|
cursor,
|
||||||
)
|
)
|
||||||
|
refund_vout: int = ci_from.getLockRefundVout(
|
||||||
|
xmr_swap.a_lock_refund_tx, xmr_swap.vkbv
|
||||||
|
)
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||||
txid=bytes.fromhex(txid),
|
txid=bytes.fromhex(txid),
|
||||||
|
vout=refund_vout,
|
||||||
)
|
)
|
||||||
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
||||||
self.commitDB()
|
self.commitDB()
|
||||||
@@ -7962,10 +7983,14 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
txid = ci_from.getTxid(xmr_swap.a_lock_refund_tx)
|
txid = ci_from.getTxid(xmr_swap.a_lock_refund_tx)
|
||||||
if TxTypes.XMR_SWAP_A_LOCK_REFUND not in bid.txns:
|
if TxTypes.XMR_SWAP_A_LOCK_REFUND not in bid.txns:
|
||||||
|
refund_vout: int = ci_from.getLockRefundVout(
|
||||||
|
xmr_swap.a_lock_refund_tx, xmr_swap.vkbv
|
||||||
|
)
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||||
txid=txid,
|
txid=txid,
|
||||||
|
vout=refund_vout,
|
||||||
)
|
)
|
||||||
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
self.saveBidInSession(bid_id, bid, cursor, xmr_swap)
|
||||||
self.commitDB()
|
self.commitDB()
|
||||||
@@ -8346,14 +8371,15 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
def _isScriptRefundMature(self, ci, offer, refund_tx_bytes, parent_tx) -> bool:
|
def _isScriptRefundMature(self, ci, offer, refund_tx_bytes, parent_tx) -> bool:
|
||||||
refund_tx = ci.loadTx(refund_tx_bytes)
|
|
||||||
if offer.lock_type in (TxLockTypes.ABS_LOCK_BLOCKS, TxLockTypes.ABS_LOCK_TIME):
|
if offer.lock_type in (TxLockTypes.ABS_LOCK_BLOCKS, TxLockTypes.ABS_LOCK_TIME):
|
||||||
return ci.isAbsLockTimeMature(refund_tx.nLockTime)
|
tx_locktime: int = ci.getTxLocktime(refund_tx_bytes)
|
||||||
|
return ci.isAbsLockTimeMature(tx_locktime)
|
||||||
if parent_tx is None or parent_tx.block_height is None:
|
if parent_tx is None or parent_tx.block_height is None:
|
||||||
return False
|
return False
|
||||||
|
txi_sequence: int = ci.getTxInSequence(refund_tx_bytes, 0)
|
||||||
return ci.isCsvLockMature(
|
return ci.isCsvLockMature(
|
||||||
offer.lock_type,
|
offer.lock_type,
|
||||||
refund_tx.vin[0].nSequence,
|
txi_sequence,
|
||||||
parent_tx.block_height,
|
parent_tx.block_height,
|
||||||
parent_tx.block_time,
|
parent_tx.block_time,
|
||||||
)
|
)
|
||||||
@@ -8641,12 +8667,33 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
f"Error trying to submit initiate refund txn: {ex}"
|
f"Error trying to submit initiate refund txn: {ex}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
should_try_refund_ptx: bool = (
|
||||||
bid.getPTxState() in (TxStates.TX_SENT, TxStates.TX_CONFIRMED)
|
bid.getPTxState() in (TxStates.TX_SENT, TxStates.TX_CONFIRMED)
|
||||||
and bid.participate_txn_refund is not None
|
and bid.participate_txn_refund is not None
|
||||||
and self._isScriptRefundMature(
|
|
||||||
ci_to, offer, bid.participate_txn_refund, bid.participate_tx
|
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
should_try_refund_ptx
|
||||||
|
and bid.participate_tx is not None
|
||||||
|
and bid.participate_tx.block_height is None
|
||||||
|
):
|
||||||
|
self.log.debug(
|
||||||
|
f"PTX: {self.logIDT(bid.participate_tx.txid)} block height not known, bid: {self.log.id(bid_id)}"
|
||||||
|
)
|
||||||
|
# An invalid ptx, won't be confirmed, check block height here
|
||||||
|
ptx_info = ci_to.getTxOutInfo(
|
||||||
|
bid.participate_tx.txid, bid.participate_tx.vout
|
||||||
|
)
|
||||||
|
if ptx_info:
|
||||||
|
bid.participate_tx.block_hash = ptx_info["block_hash"]
|
||||||
|
bid.participate_tx.block_height = ptx_info["block_height"]
|
||||||
|
bid.participate_tx.block_time = ptx_info["block_time"]
|
||||||
|
self.log.debug(
|
||||||
|
f"Found PTX block height: {bid.participate_tx.block_height}, time: {bid.participate_tx.block_time}"
|
||||||
|
)
|
||||||
|
self.saveBid(bid_id, bid)
|
||||||
|
|
||||||
|
if should_try_refund_ptx and self._isScriptRefundMature(
|
||||||
|
ci_to, offer, bid.participate_txn_refund, bid.participate_tx
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
txid = ci_to.publishTx(bid.participate_txn_refund)
|
txid = ci_to.publishTx(bid.participate_txn_refund)
|
||||||
@@ -9017,10 +9064,14 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TxTypes.XMR_SWAP_A_LOCK_REFUND not in bid.txns:
|
if TxTypes.XMR_SWAP_A_LOCK_REFUND not in bid.txns:
|
||||||
|
refund_vout: int = ci_from.getLockRefundVout(
|
||||||
|
bytes.fromhex(spend_txn_hex), xmr_swap.vkbv
|
||||||
|
)
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
||||||
bid_id=bid.bid_id,
|
bid_id=bid.bid_id,
|
||||||
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||||
txid=xmr_swap.a_lock_refund_tx_id,
|
txid=xmr_swap.a_lock_refund_tx_id,
|
||||||
|
vout=refund_vout,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.setBidError(
|
self.setBidError(
|
||||||
@@ -9140,6 +9191,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
if was_received:
|
if was_received:
|
||||||
if self.isBchXmrSwap(offer):
|
if self.isBchXmrSwap(offer):
|
||||||
# Mercy tx is sent separately
|
# Mercy tx is sent separately
|
||||||
|
# Can't set XMR_SWAP_FAILED_SWIPED, as bid should continue looking for mercy tx
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# Look for a mercy output
|
# Look for a mercy output
|
||||||
@@ -11218,7 +11270,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
refundExtraArgs = dict()
|
refundExtraArgs = dict()
|
||||||
lockExtraArgs = dict()
|
lockExtraArgs = dict()
|
||||||
if self.isBchXmrSwap(offer):
|
if self.isBchXmrSwap(offer):
|
||||||
# perform check that both lock and refund transactions have their outs pointing to correct follower address
|
# Perform check that both lock and refund transactions have their outs pointing to correct follower address
|
||||||
# and prepare extra args for validation
|
# and prepare extra args for validation
|
||||||
|
|
||||||
bch_ci = self.ci(Coins.BCH)
|
bch_ci = self.ci(Coins.BCH)
|
||||||
@@ -12975,8 +13027,8 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
self.log.info(f"Route established for bid {self.log.id(bid_id)}")
|
self.log.info(f"Route established for bid {self.log.id(bid_id)}")
|
||||||
|
|
||||||
bid, offer = self.getBidAndOffer(bid_id, cursor)
|
bid, offer = self.getBidAndOffer(bid_id, cursor)
|
||||||
ensure(bid, "Bid not found")
|
ensure(bid, f"Bid not found: {self.log.id(bid_id)}.")
|
||||||
ensure(offer, "Offer not found")
|
ensure(offer, f"Offer not found: {self.log.id(bid.offer_id)}.")
|
||||||
|
|
||||||
coin_from = Coins(offer.coin_from)
|
coin_from = Coins(offer.coin_from)
|
||||||
coin_to = Coins(offer.coin_to)
|
coin_to = Coins(offer.coin_to)
|
||||||
@@ -14082,9 +14134,9 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
walletinfo = ci.getWalletInfo()
|
walletinfo = ci.getWalletInfo()
|
||||||
rv = {
|
rv = {
|
||||||
"deposit_address": self.getCachedAddressForCoin(coin),
|
"deposit_address": self.getCachedAddressForCoin(coin),
|
||||||
"balance": ci.format_amount(walletinfo["balance"], conv_int=True),
|
"balance": ci.format_amount(walletinfo["balance"], conv_int=True, r=-1),
|
||||||
"unconfirmed": ci.format_amount(
|
"unconfirmed": ci.format_amount(
|
||||||
walletinfo["unconfirmed_balance"], conv_int=True
|
walletinfo["unconfirmed_balance"], conv_int=True, r=-1
|
||||||
),
|
),
|
||||||
"expected_seed": ci.knownWalletSeed(),
|
"expected_seed": ci.knownWalletSeed(),
|
||||||
"encrypted": walletinfo["encrypted"],
|
"encrypted": walletinfo["encrypted"],
|
||||||
@@ -14099,7 +14151,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
|
|
||||||
if "immature_balance" in walletinfo:
|
if "immature_balance" in walletinfo:
|
||||||
rv["immature"] = ci.format_amount(
|
rv["immature"] = ci.format_amount(
|
||||||
walletinfo["immature_balance"], conv_int=True
|
walletinfo["immature_balance"], conv_int=True, r=-1
|
||||||
)
|
)
|
||||||
|
|
||||||
if "locked_utxos" in walletinfo:
|
if "locked_utxos" in walletinfo:
|
||||||
@@ -15090,8 +15142,7 @@ class BasicSwap(BaseApp, BSXNetwork, UIApp):
|
|||||||
return
|
return
|
||||||
|
|
||||||
bid = self.getBid(bid_id)
|
bid = self.getBid(bid_id)
|
||||||
if bid is None:
|
ensure(bid, f"Bid not found: {self.log.id(bid_id)}.")
|
||||||
raise ValueError("Bid not found.")
|
|
||||||
|
|
||||||
bid.debug_ind = debug_ind
|
bid.debug_ind = debug_ind
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
@@ -193,6 +192,9 @@ class AdaptorSigInterface:
|
|||||||
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes) -> List[bytes]:
|
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes) -> List[bytes]:
|
||||||
return [bytes(72), b"", bytes(len(script))]
|
return [bytes(72), b"", bytes(len(script))]
|
||||||
|
|
||||||
|
def getLockRefundVout(self, lock_refund_tx_data: bytes, vbkv: bytes):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|||||||
@@ -147,7 +147,9 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
if not self.isAddressMine(address, or_watch_only=True):
|
if not self.isAddressMine(address, or_watch_only=True):
|
||||||
# Expects P2WSH nested in BIP16_P2SH
|
# Expects P2WSH nested in BIP16_P2SH
|
||||||
self.rpc("importaddress", [lock_tx_dest.hex(), "bid lock", False, True])
|
self.rpc_wallet(
|
||||||
|
"importaddress", [lock_tx_dest.hex(), "bid lock", False, True]
|
||||||
|
)
|
||||||
|
|
||||||
return address
|
return address
|
||||||
|
|
||||||
@@ -156,12 +158,20 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
def createRawFundedTransaction(
|
def createRawFundedTransaction(
|
||||||
self,
|
self,
|
||||||
addr_to: str,
|
addr_to: str | bytes,
|
||||||
amount: int,
|
amount: int,
|
||||||
sub_fee: bool = False,
|
sub_fee: bool = False,
|
||||||
lock_unspents: bool = True,
|
lock_unspents: bool = True,
|
||||||
feerate: int = None,
|
feerate: int = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
||||||
|
if isinstance(addr_to, bytes):
|
||||||
|
# addr_to is script_pubkey
|
||||||
|
tx = CTransaction()
|
||||||
|
tx.nVersion = self.txVersion()
|
||||||
|
tx.vout.append(self.txoType()(amount, addr_to))
|
||||||
|
txn = tx.serialize_without_witness().hex()
|
||||||
|
else:
|
||||||
txn = self.rpc(
|
txn = self.rpc(
|
||||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
||||||
)
|
)
|
||||||
@@ -228,6 +238,16 @@ class BCHInterface(BTCInterface):
|
|||||||
)
|
)
|
||||||
return pay_fee
|
return pay_fee
|
||||||
|
|
||||||
|
def getBLockTxo(
|
||||||
|
self,
|
||||||
|
chain_b_lock_txid: bytes,
|
||||||
|
lock_tx_vout: int,
|
||||||
|
script_pk: bytes,
|
||||||
|
) -> (int, int):
|
||||||
|
txout = self.rpc("gettxout", [chain_b_lock_txid.hex(), lock_tx_vout, True])
|
||||||
|
actual_value = self.make_int(txout["value"])
|
||||||
|
return lock_tx_vout, actual_value
|
||||||
|
|
||||||
def findTxnByHash(self, txid_hex: str):
|
def findTxnByHash(self, txid_hex: str):
|
||||||
# Only works for wallet txns
|
# Only works for wallet txns
|
||||||
try:
|
try:
|
||||||
@@ -282,7 +302,7 @@ class BCHInterface(BTCInterface):
|
|||||||
found_vout = try_vout
|
found_vout = try_vout
|
||||||
break
|
break
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e: # noqa: F841
|
||||||
# self._log.warning('gettxout {}'.format(e))
|
# self._log.warning(f"gettxout {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if found_vout is None:
|
if found_vout is None:
|
||||||
@@ -295,7 +315,7 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
# TODO: Better way?
|
# TODO: Better way?
|
||||||
if confirmations > 0:
|
if confirmations > 0:
|
||||||
block_height = self.getChainHeight() - confirmations
|
block_height = self.getChainHeight() - (confirmations - 1)
|
||||||
|
|
||||||
rv = {
|
rv = {
|
||||||
"txid": txid.hex(),
|
"txid": txid.hex(),
|
||||||
@@ -516,6 +536,7 @@ class BCHInterface(BTCInterface):
|
|||||||
tx_lock = self.loadTx(tx_lock_bytes)
|
tx_lock = self.loadTx(tx_lock_bytes)
|
||||||
|
|
||||||
output_script = self.getScriptDest(script_lock)
|
output_script = self.getScriptDest(script_lock)
|
||||||
|
|
||||||
locked_n = findOutput(tx_lock, output_script)
|
locked_n = findOutput(tx_lock, output_script)
|
||||||
ensure(locked_n is not None, "Output not found in tx")
|
ensure(locked_n is not None, "Output not found in tx")
|
||||||
locked_coin = tx_lock.vout[locked_n].nValue
|
locked_coin = tx_lock.vout[locked_n].nValue
|
||||||
@@ -1134,7 +1155,7 @@ class BCHInterface(BTCInterface):
|
|||||||
refund_output_value = refund_swipe_tx.vout[0].nValue
|
refund_output_value = refund_swipe_tx.vout[0].nValue
|
||||||
refund_output_script = refund_swipe_tx.vout[0].scriptPubKey
|
refund_output_script = refund_swipe_tx.vout[0].scriptPubKey
|
||||||
|
|
||||||
# mercy transaction size consisting of one input of freshly received funds,
|
# Mercy transaction size consisting of one input of freshly received funds,
|
||||||
# one op_return with mercy information, a dust output to the leader and change back to the follower
|
# one op_return with mercy information, a dust output to the leader and change back to the follower
|
||||||
tx_size = 275
|
tx_size = 275
|
||||||
dust_limit = 546
|
dust_limit = 546
|
||||||
|
|||||||
+120
-53
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
@@ -508,6 +507,14 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
return height
|
return height
|
||||||
return self.rpc("getblockcount")
|
return self.rpc("getblockcount")
|
||||||
|
|
||||||
|
def getTxLocktime(self, tx_data: bytes) -> int:
|
||||||
|
tx_obj = self.loadTx(tx_data)
|
||||||
|
return tx_obj.nLockTime
|
||||||
|
|
||||||
|
def getTxInSequence(self, tx_data: bytes, vout: int) -> int:
|
||||||
|
tx_obj = self.loadTx(tx_data)
|
||||||
|
return tx_obj.vin[vout].nSequence
|
||||||
|
|
||||||
def getChainMedianTime(self) -> int:
|
def getChainMedianTime(self) -> int:
|
||||||
if self.useBackend():
|
if self.useBackend():
|
||||||
import struct
|
import struct
|
||||||
@@ -601,7 +608,7 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
block_hash = sha256(sha256(header_bytes))[::-1].hex()
|
block_hash = sha256(sha256(header_bytes))[::-1].hex()
|
||||||
return {"height": height, "hash": block_hash, "time": block_time}
|
return {"height": height, "hash": block_hash, "time": block_time}
|
||||||
|
|
||||||
def getBlockHeader(self, block_hash):
|
def getBlockHeader(self, block_hash: str) -> dict:
|
||||||
if self._connection_type == "electrum":
|
if self._connection_type == "electrum":
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"getBlockHeader by hash not available in electrum mode"
|
"getBlockHeader by hash not available in electrum mode"
|
||||||
@@ -2785,6 +2792,47 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
)
|
)
|
||||||
return pay_fee
|
return pay_fee
|
||||||
|
|
||||||
|
def getBLockTxo(
|
||||||
|
self,
|
||||||
|
chain_b_lock_txid: bytes,
|
||||||
|
lock_tx_vout: int,
|
||||||
|
script_pk: bytes,
|
||||||
|
) -> (int, int):
|
||||||
|
if self.useBackend():
|
||||||
|
backend = self.getBackend()
|
||||||
|
tx_hex = backend.getTransactionRaw(chain_b_lock_txid.hex())
|
||||||
|
if tx_hex:
|
||||||
|
lock_tx = self.loadTx(bytes.fromhex(tx_hex))
|
||||||
|
locked_n = findOutput(lock_tx, script_pk)
|
||||||
|
if locked_n is not None:
|
||||||
|
actual_value = lock_tx.vout[locked_n].nValue
|
||||||
|
else:
|
||||||
|
self._log.error(
|
||||||
|
f"spendBLockTx: Output not found in tx {self._log.id(chain_b_lock_txid)}, "
|
||||||
|
f"script_pk={script_pk.hex()}, num_outputs={len(lock_tx.vout)}"
|
||||||
|
)
|
||||||
|
for i, out in enumerate(lock_tx.vout):
|
||||||
|
self._log.debug(
|
||||||
|
f" vout[{i}]: value={out.nValue}, scriptPubKey={out.scriptPubKey.hex()}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._log.warning(
|
||||||
|
f"spendBLockTx: Failed to fetch tx {self._log.id(chain_b_lock_txid)} from backend"
|
||||||
|
)
|
||||||
|
locked_n = lock_tx_vout
|
||||||
|
return locked_n, actual_value
|
||||||
|
wtx = self.rpc_wallet_watch(
|
||||||
|
"gettransaction",
|
||||||
|
[
|
||||||
|
chain_b_lock_txid.hex(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
lock_tx = self.loadTx(bytes.fromhex(wtx["hex"]))
|
||||||
|
locked_n = findOutput(lock_tx, script_pk)
|
||||||
|
if locked_n is not None:
|
||||||
|
actual_value = lock_tx.vout[locked_n].nValue
|
||||||
|
return locked_n, actual_value
|
||||||
|
|
||||||
def spendBLockTx(
|
def spendBLockTx(
|
||||||
self,
|
self,
|
||||||
chain_b_lock_txid: bytes,
|
chain_b_lock_txid: bytes,
|
||||||
@@ -2798,48 +2846,14 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
lock_tx_vout=None,
|
lock_tx_vout=None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"spendBLockTx: {} {}\n".format(
|
f"spendBLockTx: {self._log.id(chain_b_lock_txid)} {lock_tx_vout}\n"
|
||||||
self._log.id(chain_b_lock_txid), lock_tx_vout
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
Kbs = self.getPubkey(kbs)
|
Kbs = self.getPubkey(kbs)
|
||||||
script_pk = self.getPkDest(Kbs)
|
script_pk = self.getPkDest(Kbs)
|
||||||
|
|
||||||
locked_n = None
|
locked_n, actual_value = self.getBLockTxo(
|
||||||
actual_value = None
|
chain_b_lock_txid, lock_tx_vout, script_pk
|
||||||
if self.useBackend():
|
|
||||||
backend = self.getBackend()
|
|
||||||
tx_hex = backend.getTransactionRaw(chain_b_lock_txid.hex())
|
|
||||||
if tx_hex:
|
|
||||||
lock_tx = self.loadTx(bytes.fromhex(tx_hex))
|
|
||||||
locked_n = findOutput(lock_tx, script_pk)
|
|
||||||
if locked_n is not None:
|
|
||||||
actual_value = lock_tx.vout[locked_n].nValue
|
|
||||||
else:
|
|
||||||
self._log.error(
|
|
||||||
f"spendBLockTx: Output not found in tx {chain_b_lock_txid.hex()}, "
|
|
||||||
f"script_pk={script_pk.hex()}, num_outputs={len(lock_tx.vout)}"
|
|
||||||
)
|
)
|
||||||
for i, out in enumerate(lock_tx.vout):
|
|
||||||
self._log.debug(
|
|
||||||
f" vout[{i}]: value={out.nValue}, scriptPubKey={out.scriptPubKey.hex()}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._log.warning(
|
|
||||||
f"spendBLockTx: Failed to fetch tx {chain_b_lock_txid.hex()} from backend"
|
|
||||||
)
|
|
||||||
locked_n = lock_tx_vout
|
|
||||||
else:
|
|
||||||
wtx = self.rpc_wallet_watch(
|
|
||||||
"gettransaction",
|
|
||||||
[
|
|
||||||
chain_b_lock_txid.hex(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
lock_tx = self.loadTx(bytes.fromhex(wtx["hex"]))
|
|
||||||
locked_n = findOutput(lock_tx, script_pk)
|
|
||||||
if locked_n is not None:
|
|
||||||
actual_value = lock_tx.vout[locked_n].nValue
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
locked_n is not None
|
locked_n is not None
|
||||||
@@ -2848,7 +2862,7 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
):
|
):
|
||||||
self._log.warning(
|
self._log.warning(
|
||||||
f"spendBLockTx: Stored vout {lock_tx_vout} differs from actual vout {locked_n} "
|
f"spendBLockTx: Stored vout {lock_tx_vout} differs from actual vout {locked_n} "
|
||||||
f"for tx {chain_b_lock_txid.hex()}"
|
f"for tx {self._log.id(chain_b_lock_txid)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
ensure(locked_n is not None, "Output not found in tx")
|
ensure(locked_n is not None, "Output not found in tx")
|
||||||
@@ -2938,13 +2952,9 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
# Add watchonly address and rescan if required
|
# Add watchonly address and rescan if required
|
||||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||||
self.importWatchOnlyAddress(dest_address, "bid")
|
self.importWatchOnlyAddress(dest_address, "bid")
|
||||||
|
self._log.info(f"Imported watch-only addr: {self._log.addr(dest_address)}")
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Imported watch-only addr: {}".format(self._log.addr(dest_address))
|
f"Rescanning {self.coin_name()} chain from height: {rescan_from}"
|
||||||
)
|
|
||||||
self._log.info(
|
|
||||||
"Rescanning {} chain from height: {}".format(
|
|
||||||
self.coin_name(), rescan_from
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.rpc_wallet("rescanblockchain", [rescan_from])
|
self.rpc_wallet("rescanblockchain", [rescan_from])
|
||||||
|
|
||||||
@@ -3657,7 +3667,7 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
continue
|
continue
|
||||||
if "desc" in u:
|
if "desc" in u:
|
||||||
desc = u["desc"]
|
desc = u["desc"]
|
||||||
if self.using_segwit:
|
if self.using_segwit():
|
||||||
if self.use_p2shp2wsh():
|
if self.use_p2shp2wsh():
|
||||||
if not desc.startswith("sh(wpkh"):
|
if not desc.startswith("sh(wpkh"):
|
||||||
continue
|
continue
|
||||||
@@ -3818,7 +3828,7 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
ensure(
|
ensure(
|
||||||
sign_for_addr is not None,
|
sign_for_addr is not None,
|
||||||
"Could not find address with enough funds for proof",
|
f"Could not find {self.ticker()} address with enough funds for proof",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._log.debug(f"sign_for_addr {sign_for_addr}")
|
self._log.debug(f"sign_for_addr {sign_for_addr}")
|
||||||
@@ -4457,6 +4467,8 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def isTxExistsError(self, err_str: str) -> bool:
|
def isTxExistsError(self, err_str: str) -> bool:
|
||||||
|
if self._connection_type == "electrum":
|
||||||
|
return "Transaction outputs already in utxo set" in err_str
|
||||||
return "Transaction already in block chain" in err_str
|
return "Transaction already in block chain" in err_str
|
||||||
|
|
||||||
def isTxNonFinalError(self, err_str: str) -> bool:
|
def isTxNonFinalError(self, err_str: str) -> bool:
|
||||||
@@ -4511,7 +4523,7 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
self._log.id(bytes.fromhex(tx["txid"]))
|
self._log.id(bytes.fromhex(tx["txid"]))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.publishTx(tx_signed)
|
self.publishTx(bytes.fromhex(tx_signed))
|
||||||
|
|
||||||
return tx["txid"]
|
return tx["txid"]
|
||||||
|
|
||||||
@@ -4549,10 +4561,65 @@ class BTCInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
return bytes.fromhex(txi_txid_hex), fee_rate
|
return bytes.fromhex(txi_txid_hex), fee_rate
|
||||||
|
|
||||||
|
def _getTxOutInfoElectrum(self, txid: bytes, n: int, include_mempool: bool = False):
|
||||||
|
backend = self.getBackend()
|
||||||
|
if not backend:
|
||||||
|
return None
|
||||||
|
|
||||||
def testBTCInterface():
|
try:
|
||||||
print("TODO: testBTCInterface")
|
tx_info = backend.getTransaction(txid.hex())
|
||||||
|
if "blockhash" not in tx_info:
|
||||||
|
return None
|
||||||
|
confirmations: int = (
|
||||||
|
0 if "confirmations" not in tx_info else tx_info["confirmations"]
|
||||||
|
)
|
||||||
|
if confirmations < 1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
chain_tip_height = self.getChainHeight()
|
||||||
|
block_height: int = chain_tip_height - (confirmations - 1)
|
||||||
|
block_hash: bytes = bytes.fromhex(tx_info["blockhash"])
|
||||||
|
return {
|
||||||
|
"block_hash": block_hash,
|
||||||
|
"block_height": block_height,
|
||||||
|
"block_time": tx_info["blocktime"],
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
self._log.debug(f"_findTxnByHashElectrum failed: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def getTxOutInfo(
|
||||||
testBTCInterface()
|
self, txid: bytes, n: int, include_mempool: bool = False
|
||||||
|
) -> dict():
|
||||||
|
if self._connection_type == "electrum":
|
||||||
|
return self._getTxOutInfoElectrum(txid, n, include_mempool)
|
||||||
|
try:
|
||||||
|
txout = self.rpc("gettxout", [txid.hex(), n, include_mempool])
|
||||||
|
confirmations: int = (
|
||||||
|
0 if "confirmations" not in txout else txout["confirmations"]
|
||||||
|
)
|
||||||
|
if confirmations < 1:
|
||||||
|
return None
|
||||||
|
chain_tip_height: int = 0
|
||||||
|
if "bestblock" in txout:
|
||||||
|
bestheader_info = self.getBlockHeader(txout["bestblock"])
|
||||||
|
chain_tip_height = bestheader_info["height"]
|
||||||
|
else:
|
||||||
|
chain_tip_height = self.getChainHeight()
|
||||||
|
|
||||||
|
if confirmations == 1:
|
||||||
|
header_info = bestheader_info
|
||||||
|
else:
|
||||||
|
block_height: int = chain_tip_height - (confirmations - 1)
|
||||||
|
header_info = self.getBlockHeaderFromHeight(block_height)
|
||||||
|
|
||||||
|
block_hash: bytes = bytes.fromhex(header_info["hash"])
|
||||||
|
return {
|
||||||
|
"block_hash": block_hash,
|
||||||
|
"block_height": header_info["height"],
|
||||||
|
"block_time": header_info["time"],
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e: # noqa: F841
|
||||||
|
# self._log.warning(f"gettxout {e}")
|
||||||
|
return None
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022-2024 tecnovert
|
# Copyright (c) 2022-2024 tecnovert
|
||||||
|
|||||||
+130
-11
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
@@ -13,7 +12,7 @@ import logging
|
|||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
from basicswap.basicswap_util import getVoutByScriptPubKey, TxLockTypes
|
from basicswap.basicswap_util import getVoutByScriptPubKey, TxLockTypes
|
||||||
from basicswap.chainparams import Coins
|
from basicswap.chainparams import Coins
|
||||||
@@ -419,8 +418,10 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
return bci
|
return bci
|
||||||
|
|
||||||
|
def getBlockHeader(self, block_hash: str) -> dict:
|
||||||
|
return self.rpc("getblockheader", [block_hash])
|
||||||
|
|
||||||
def getWalletInfo(self):
|
def getWalletInfo(self):
|
||||||
rv = {}
|
|
||||||
rv = self.rpc_wallet("getinfo")
|
rv = self.rpc_wallet("getinfo")
|
||||||
wi = self.rpc_wallet("walletinfo")
|
wi = self.rpc_wallet("walletinfo")
|
||||||
balances = self.rpc_wallet("getbalance")
|
balances = self.rpc_wallet("getbalance")
|
||||||
@@ -595,7 +596,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
override_feerate = chain_client_settings.get("override_feerate", None)
|
override_feerate = chain_client_settings.get("override_feerate", None)
|
||||||
if override_feerate:
|
if override_feerate:
|
||||||
self._log.debug(
|
self._log.debug(
|
||||||
"Fee rate override used for %s: %f", self.coin_name(), override_feerate
|
f"Fee rate override used for {self.coin_name()}: {override_feerate}"
|
||||||
)
|
)
|
||||||
return override_feerate, "override_feerate"
|
return override_feerate, "override_feerate"
|
||||||
|
|
||||||
@@ -919,7 +920,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
found_vout = try_vout
|
found_vout = try_vout
|
||||||
break
|
break
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e: # noqa: F841
|
||||||
# self._log.warning('gettxout {}'.format(e))
|
# self._log.warning(f"gettxout {e})
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if found_vout is None:
|
if found_vout is None:
|
||||||
@@ -932,7 +933,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
# TODO: Better way?
|
# TODO: Better way?
|
||||||
if confirmations > 0:
|
if confirmations > 0:
|
||||||
block_height = self.getChainHeight() - confirmations
|
block_height = self.getChainHeight() - (confirmations - 1)
|
||||||
|
|
||||||
rv = {
|
rv = {
|
||||||
"txid": txid.hex(),
|
"txid": txid.hex(),
|
||||||
@@ -999,6 +1000,10 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
tx.vout.append(self.txoType()(output_value, script))
|
tx.vout.append(self.txoType()(output_value, script))
|
||||||
return tx.serialize().hex()
|
return tx.serialize().hex()
|
||||||
|
|
||||||
|
def ensureFunds(self, amount: int) -> None:
|
||||||
|
if self.getSpendableBalance() < amount:
|
||||||
|
raise ValueError("Balance too low")
|
||||||
|
|
||||||
def verifyRawTransaction(self, tx_hex: str, prevouts):
|
def verifyRawTransaction(self, tx_hex: str, prevouts):
|
||||||
inputs_valid: bool = True
|
inputs_valid: bool = True
|
||||||
validscripts: int = 0
|
validscripts: int = 0
|
||||||
@@ -1151,6 +1156,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
pay_fee = round(tx_fee_rate * size / 1000)
|
pay_fee = round(tx_fee_rate * size / 1000)
|
||||||
tx.vout[0].value = locked_coin - pay_fee
|
tx.vout[0].value = locked_coin - pay_fee
|
||||||
|
|
||||||
@@ -1202,6 +1208,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
pay_fee = round(tx_fee_rate * size / 1000)
|
pay_fee = round(tx_fee_rate * size / 1000)
|
||||||
tx.vout[0].value = locked_coin - pay_fee
|
tx.vout[0].value = locked_coin - pay_fee
|
||||||
|
|
||||||
@@ -1253,6 +1260,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
script_lock_refund
|
script_lock_refund
|
||||||
)
|
)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
pay_fee = round(tx_fee_rate * size / 1000)
|
pay_fee = round(tx_fee_rate * size / 1000)
|
||||||
tx.vout[0].value = locked_coin - pay_fee
|
tx.vout[0].value = locked_coin - pay_fee
|
||||||
|
|
||||||
@@ -1337,6 +1345,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
assert fee_paid > 0
|
assert fee_paid > 0
|
||||||
|
|
||||||
size = len(tx.serialize()) + add_witness_bytes
|
size = len(tx.serialize()) + add_witness_bytes
|
||||||
|
size += 1
|
||||||
fee_rate_paid = fee_paid * 1000 // size
|
fee_rate_paid = fee_paid * 1000 // size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
@@ -1398,6 +1407,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(lock_tx_script)
|
dummy_witness_stack = self.getScriptLockTxDummyWitness(lock_tx_script)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
fee_rate_paid = fee_paid * 1000 // size
|
fee_rate_paid = fee_paid * 1000 // size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
@@ -1470,6 +1480,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(prevout_script)
|
dummy_witness_stack = self.getScriptLockTxDummyWitness(prevout_script)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
fee_rate_paid = fee_paid * 1000 // size
|
fee_rate_paid = fee_paid * 1000 // size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
@@ -1531,6 +1542,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
prevout_script
|
prevout_script
|
||||||
)
|
)
|
||||||
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
size = len(self.setTxSignature(tx.serialize(), dummy_witness_stack))
|
||||||
|
size += 1
|
||||||
fee_rate_paid = fee_paid * 1000 // size
|
fee_rate_paid = fee_paid * 1000 // size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
@@ -1781,13 +1793,16 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
spend_actual_balance: bool = False,
|
spend_actual_balance: bool = False,
|
||||||
lock_tx_vout=None,
|
lock_tx_vout=None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
self._log.info(
|
||||||
|
f"spendBLockTx: {self._log.id(chain_b_lock_txid)} {lock_tx_vout}\n"
|
||||||
|
)
|
||||||
|
|
||||||
Kbs = self.getPubkey(kbs)
|
Kbs = self.getPubkey(kbs)
|
||||||
script_pk = self.getPkDest(Kbs)
|
script_pk = self.getPkDest(Kbs)
|
||||||
|
|
||||||
locked_n = None
|
locked_n = None
|
||||||
actual_value = None
|
actual_value = None
|
||||||
|
try:
|
||||||
wtx = self.rpc_wallet(
|
wtx = self.rpc_wallet(
|
||||||
"gettransaction",
|
"gettransaction",
|
||||||
[
|
[
|
||||||
@@ -1800,13 +1815,19 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
actual_value = lock_tx.vout[locked_n].value
|
actual_value = lock_tx.vout[locked_n].value
|
||||||
else:
|
else:
|
||||||
self._log.error(
|
self._log.error(
|
||||||
f"spendBLockTx: Output not found in tx {chain_b_lock_txid.hex()}, "
|
f"spendBLockTx: Output not found in tx {self._log.id(chain_b_lock_txid)}, "
|
||||||
f"script_pk={script_pk.hex()}, num_outputs={len(lock_tx.vout)}"
|
f"script_pk={script_pk.hex()}, num_outputs={len(lock_tx.vout)}"
|
||||||
)
|
)
|
||||||
for i, out in enumerate(lock_tx.vout):
|
for i, out in enumerate(lock_tx.vout):
|
||||||
self._log.debug(
|
self._log.debug(
|
||||||
f" vout[{i}]: value={out.value}, scriptPubKey={out.scriptPubKey.hex()}"
|
f" vout[{i}]: value={out.value}, scriptPubKey={out.scriptPubKey.hex()}"
|
||||||
)
|
)
|
||||||
|
except Exception as e: # noqa: F841
|
||||||
|
txout = self.rpc(
|
||||||
|
"gettxout", [chain_b_lock_txid.hex(), lock_tx_vout, 0, True]
|
||||||
|
)
|
||||||
|
actual_value = self.make_int(txout["value"])
|
||||||
|
locked_n = lock_tx_vout
|
||||||
|
|
||||||
if (
|
if (
|
||||||
locked_n is not None
|
locked_n is not None
|
||||||
@@ -1815,7 +1836,7 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
):
|
):
|
||||||
self._log.warning(
|
self._log.warning(
|
||||||
f"spendBLockTx: Stored vout {lock_tx_vout} differs from actual vout {locked_n} "
|
f"spendBLockTx: Stored vout {lock_tx_vout} differs from actual vout {locked_n} "
|
||||||
f"for tx {chain_b_lock_txid.hex()}"
|
f"for tx {self._log.id(chain_b_lock_txid)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
ensure(locked_n is not None, "Output not found in tx")
|
ensure(locked_n is not None, "Output not found in tx")
|
||||||
@@ -1851,14 +1872,14 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
try:
|
try:
|
||||||
txout = self.rpc("gettxout", [txid_hex, 0, 0, True])
|
txout = self.rpc("gettxout", [txid_hex, 0, 0, True])
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e: # noqa: F841
|
||||||
# self._log.warning('gettxout {}'.format(e))
|
# self._log.warning(f"gettxout {e}"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
confirmations: int = (
|
confirmations: int = (
|
||||||
0 if "confirmations" not in txout else txout["confirmations"]
|
0 if "confirmations" not in txout else txout["confirmations"]
|
||||||
)
|
)
|
||||||
if confirmations >= self.blocks_confirmed:
|
if confirmations >= self.blocks_confirmed:
|
||||||
block_height = self.getChainHeight() - confirmations # TODO: Better way?
|
block_height = self.getChainHeight() - (confirmations - 1)
|
||||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1873,3 +1894,101 @@ class DCRInterface(FeeValidator, Secp256k1Interface):
|
|||||||
|
|
||||||
def isTxNonFinalError(self, err_str: str) -> bool:
|
def isTxNonFinalError(self, err_str: str) -> bool:
|
||||||
return "locks on inputs not met" in err_str
|
return "locks on inputs not met" in err_str
|
||||||
|
|
||||||
|
def getChainMedianTime(self) -> int:
|
||||||
|
bestblockhash = self.rpc("getbestblockhash")
|
||||||
|
bestblockheader = self.rpc(
|
||||||
|
"getblockheader",
|
||||||
|
[
|
||||||
|
bestblockhash,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return bestblockheader["mediantime"]
|
||||||
|
|
||||||
|
def getTxLocktime(self, tx_data: bytes) -> int:
|
||||||
|
tx_obj = self.loadTx(tx_data)
|
||||||
|
return tx_obj.locktime
|
||||||
|
|
||||||
|
def getTxInSequence(self, tx_data: bytes, vout: int) -> int:
|
||||||
|
tx_obj = self.loadTx(tx_data)
|
||||||
|
return tx_obj.vin[vout].sequence
|
||||||
|
|
||||||
|
def isCsvLockMature(
|
||||||
|
self,
|
||||||
|
lock_type: int,
|
||||||
|
encoded_sequence: int,
|
||||||
|
parent_block_height: Optional[int],
|
||||||
|
parent_block_time: Optional[int],
|
||||||
|
chain_height: Optional[int] = None,
|
||||||
|
chain_mtp: Optional[int] = None,
|
||||||
|
) -> bool:
|
||||||
|
if parent_block_height is None or parent_block_height < 1:
|
||||||
|
return False
|
||||||
|
lock_value: int = self.decodeSequence(encoded_sequence)
|
||||||
|
if lock_type == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
|
||||||
|
if chain_height is None:
|
||||||
|
chain_height = self.getChainHeight()
|
||||||
|
return chain_height + 1 >= parent_block_height + lock_value
|
||||||
|
if lock_type == TxLockTypes.SEQUENCE_LOCK_TIME:
|
||||||
|
if parent_block_time is None or parent_block_time < 1:
|
||||||
|
return False
|
||||||
|
if chain_mtp is None:
|
||||||
|
chain_mtp = self.getChainMedianTime()
|
||||||
|
return chain_mtp >= parent_block_time + lock_value
|
||||||
|
raise ValueError(f"Unknown lock type {lock_type}")
|
||||||
|
|
||||||
|
def isAbsLockTimeMature(
|
||||||
|
self,
|
||||||
|
nlocktime: int,
|
||||||
|
chain_height: Optional[int] = None,
|
||||||
|
chain_mtp: Optional[int] = None,
|
||||||
|
) -> bool:
|
||||||
|
if nlocktime == 0:
|
||||||
|
return True
|
||||||
|
if nlocktime < 500000000:
|
||||||
|
if chain_height is None:
|
||||||
|
chain_height = self.getChainHeight()
|
||||||
|
return chain_height + 1 >= nlocktime
|
||||||
|
if chain_mtp is None:
|
||||||
|
chain_mtp = self.getChainMedianTime()
|
||||||
|
return chain_mtp >= nlocktime
|
||||||
|
|
||||||
|
def getTxOutInfo(
|
||||||
|
self, txid: bytes, n: int, include_mempool: bool = False
|
||||||
|
) -> dict():
|
||||||
|
try:
|
||||||
|
txout = self.rpc("gettxout", [txid.hex(), n, 0, include_mempool])
|
||||||
|
confirmations: int = (
|
||||||
|
0 if "confirmations" not in txout else txout["confirmations"]
|
||||||
|
)
|
||||||
|
if confirmations < 1:
|
||||||
|
return None
|
||||||
|
chain_tip_height: int = 0
|
||||||
|
if "bestblock" in txout:
|
||||||
|
bestheader_info = self.getBlockHeader(txout["bestblock"])
|
||||||
|
chain_tip_height = bestheader_info["height"]
|
||||||
|
else:
|
||||||
|
chain_tip_height = self.getChainHeight()
|
||||||
|
|
||||||
|
if confirmations == 1:
|
||||||
|
header_info = bestheader_info
|
||||||
|
else:
|
||||||
|
block_height: int = chain_tip_height - (confirmations - 1)
|
||||||
|
header_info = self.getBlockHeaderFromHeight(block_height)
|
||||||
|
|
||||||
|
block_hash: bytes = bytes.fromhex(header_info["hash"])
|
||||||
|
return {
|
||||||
|
"block_hash": block_hash,
|
||||||
|
"block_height": header_info["height"],
|
||||||
|
"block_time": header_info["time"],
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e: # noqa: F841
|
||||||
|
# self._log.warning(f"gettxout {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_transient_error(self, ex) -> bool:
|
||||||
|
str_error: str = str(ex).lower()
|
||||||
|
if "no information for transaction" in str_error:
|
||||||
|
return True
|
||||||
|
return super().is_transient_error(ex)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
|
# 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.
|
||||||
|
|
||||||
@@ -9,10 +10,10 @@ import traceback
|
|||||||
from basicswap.rpc import Jsonrpc
|
from basicswap.rpc import Jsonrpc
|
||||||
|
|
||||||
|
|
||||||
def callrpc(rpc_port, auth, method, params=[], host="127.0.0.1"):
|
def callrpc(rpc_port, auth, method, params=[], host="127.0.0.1", timeout=None):
|
||||||
try:
|
try:
|
||||||
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
||||||
x = Jsonrpc(url)
|
x = Jsonrpc(url, timeout=timeout if timeout else 10)
|
||||||
x.__handler = None
|
x.__handler = None
|
||||||
v = x.json_request(method, params)
|
v = x.json_request(method, params)
|
||||||
x.close()
|
x.close()
|
||||||
@@ -41,7 +42,7 @@ def make_rpc_func(port, auth, host="127.0.0.1"):
|
|||||||
auth = auth
|
auth = auth
|
||||||
host = host
|
host = host
|
||||||
|
|
||||||
def rpc_func(method, params=None):
|
def rpc_func(method, params=None, timeout=None):
|
||||||
return callrpc(port, auth, method, params, host)
|
return callrpc(port, auth, method, params, host, timeout=timeout)
|
||||||
|
|
||||||
return rpc_func
|
return rpc_func
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 The BasicSwap developers
|
# Copyright (c) 2024 The BasicSwap developers
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024-2026 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022-2023 tecnovert
|
# Copyright (c) 2022-2023 tecnovert
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2023 tecnovert
|
# Copyright (c) 2020-2023 tecnovert
|
||||||
@@ -103,7 +102,7 @@ class LTCInterface(BTCInterface):
|
|||||||
continue
|
continue
|
||||||
if "desc" in u:
|
if "desc" in u:
|
||||||
desc = u["desc"]
|
desc = u["desc"]
|
||||||
if self.using_segwit:
|
if self.using_segwit():
|
||||||
if self.use_p2shp2wsh():
|
if self.use_p2shp2wsh():
|
||||||
if not desc.startswith("sh(wpkh"):
|
if not desc.startswith("sh(wpkh"):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2023 tecnovert
|
# Copyright (c) 2023 tecnovert
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2022 tecnovert
|
# Copyright (c) 2020-2022 tecnovert
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
@@ -1300,6 +1299,17 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
||||||
)["hex"]
|
)["hex"]
|
||||||
|
|
||||||
|
def getLockRefundVout(self, lock_refund_tx_data: bytes, vkbv: bytes):
|
||||||
|
lock_refund_tx_obj = self.rpc(
|
||||||
|
"decoderawtransaction", [lock_refund_tx_data.hex()]
|
||||||
|
)
|
||||||
|
# Nonce is derived from vkbv
|
||||||
|
nonce = self.getScriptLockRefundTxNonce(vkbv)
|
||||||
|
|
||||||
|
# Find the output of the lock refund tx to spend
|
||||||
|
spend_n, input_blinded_info = self.findOutputByNonce(lock_refund_tx_obj, nonce)
|
||||||
|
return spend_n
|
||||||
|
|
||||||
|
|
||||||
class PARTInterfaceAnon(PARTInterface):
|
class PARTInterfaceAnon(PARTInterface):
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2021 tecnovert
|
# Copyright (c) 2021 tecnovert
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022 tecnovert
|
# Copyright (c) 2022 tecnovert
|
||||||
@@ -171,3 +170,13 @@ class PIVXInterface(BTCInterface):
|
|||||||
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
||||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def getChainMedianTime(self) -> int:
|
||||||
|
bestblockhash = self.rpc("getbestblockhash")
|
||||||
|
bestblockheader = self.rpc(
|
||||||
|
"getblockheader",
|
||||||
|
[
|
||||||
|
bestblockhash,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return bestblockheader["mediantime"]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024 The Basicswap developers
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
try:
|
try:
|
||||||
use_cursor = self.openDB(cursor)
|
use_cursor = self.openDB(cursor)
|
||||||
bid, xmr_swap = self.getXmrBidFromSession(use_cursor, bid_id)
|
bid, xmr_swap = self.getXmrBidFromSession(use_cursor, bid_id)
|
||||||
ensure(bid, "Bid not found: {}.".format(bid_id.hex()))
|
ensure(bid, f"Bid not found: {self.log.id(bid_id)}.")
|
||||||
ensure(xmr_swap, "Adaptor-sig swap not found: {}.".format(bid_id.hex()))
|
ensure(xmr_swap, f"Adaptor-sig swap not found: {self.log.id(bid_id)}.")
|
||||||
offer, xmr_offer = self.getXmrOfferFromSession(use_cursor, bid.offer_id)
|
offer, xmr_offer = self.getXmrOfferFromSession(use_cursor, bid.offer_id)
|
||||||
ensure(offer, "Offer not found: {}.".format(bid.offer_id.hex()))
|
ensure(offer, f"Offer not found: {self.log.id(bid.offer_id)}.")
|
||||||
ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
|
ensure(xmr_offer, f"Adaptor-sig offer not found: {self.log.id(bid.offer_id)}.")
|
||||||
|
|
||||||
# The no-script coin is always the follower
|
# The no-script coin is always the follower
|
||||||
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
|
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
|
||||||
@@ -106,7 +106,10 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
address_to = self.getReceiveAddressFromPool(
|
address_to = self.getReceiveAddressFromPool(
|
||||||
base_coin_to, bid_id, TxTypes.XMR_SWAP_B_LOCK_SPEND, use_cursor
|
base_coin_to, bid_id, TxTypes.XMR_SWAP_B_LOCK_SPEND, use_cursor
|
||||||
)
|
)
|
||||||
amount = bid.amount_to
|
amount: int = bid.amount_to
|
||||||
|
chain_b_fee_rate: int = (
|
||||||
|
xmr_offer.a_fee_rate if reverse_bid else xmr_offer.b_fee_rate
|
||||||
|
)
|
||||||
lock_tx_vout = bid.getLockTXBVout()
|
lock_tx_vout = bid.getLockTXBVout()
|
||||||
txid = ci_follower.spendBLockTx(
|
txid = ci_follower.spendBLockTx(
|
||||||
xmr_swap.b_lock_tx_id,
|
xmr_swap.b_lock_tx_id,
|
||||||
@@ -114,7 +117,7 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
xmr_swap.vkbv,
|
xmr_swap.vkbv,
|
||||||
vkbs,
|
vkbs,
|
||||||
amount,
|
amount,
|
||||||
xmr_offer.b_fee_rate,
|
chain_b_fee_rate,
|
||||||
bid.chain_b_height_start,
|
bid.chain_b_height_start,
|
||||||
spend_actual_balance=True,
|
spend_actual_balance=True,
|
||||||
lock_tx_vout=lock_tx_vout,
|
lock_tx_vout=lock_tx_vout,
|
||||||
@@ -209,7 +212,7 @@ class XmrSwapInterface(ProtocolInterface):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def genScriptLockTxScript(self, ci, Kal: bytes, Kaf: bytes, **kwargs) -> CScript:
|
def genScriptLockTxScript(self, ci, Kal: bytes, Kaf: bytes, **kwargs) -> CScript:
|
||||||
# fallthrough to ci if genScriptLockTxScript is implemented there
|
# Fallthrough to ci if genScriptLockTxScript is implemented there
|
||||||
if hasattr(ci, "genScriptLockTxScript") and callable(ci.genScriptLockTxScript):
|
if hasattr(ci, "genScriptLockTxScript") and callable(ci.genScriptLockTxScript):
|
||||||
return ci.genScriptLockTxScript(ci, Kal, Kaf, **kwargs)
|
return ci.genScriptLockTxScript(ci, Kal, Kaf, **kwargs)
|
||||||
|
|
||||||
@@ -221,6 +224,11 @@ class XmrSwapInterface(ProtocolInterface):
|
|||||||
def getFundedInitiateTxTemplate(
|
def getFundedInitiateTxTemplate(
|
||||||
self, ci, amount: int, sub_fee: bool, feerate: int = None
|
self, ci, amount: int, sub_fee: bool, feerate: int = None
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
|
if ci.coin_type() == Coins.BCH:
|
||||||
|
# Workaround, BCH getScriptDest() uses OP_HASH256
|
||||||
|
script: bytes = self.getMockScript()
|
||||||
|
addr_to: bytes = ci.getScriptDest(script)
|
||||||
|
else:
|
||||||
addr_to = self.getMockScriptAddr(ci)
|
addr_to = self.getMockScriptAddr(ci)
|
||||||
funded_tx = ci.createRawFundedTransaction(
|
funded_tx = ci.createRawFundedTransaction(
|
||||||
addr_to, amount, sub_fee, lock_unspents=False, feerate=feerate
|
addr_to, amount, sub_fee, lock_unspents=False, feerate=feerate
|
||||||
@@ -247,8 +255,12 @@ class XmrSwapInterface(ProtocolInterface):
|
|||||||
return lock_vout
|
return lock_vout
|
||||||
|
|
||||||
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray:
|
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray:
|
||||||
mock_txo_script = self.getMockScriptScriptPubkey(ci)
|
if ci.coin_type() == Coins.BCH:
|
||||||
real_txo_script = ci.getScriptDest(script)
|
mock_script: bytes = self.getMockScript()
|
||||||
|
mock_txo_script: bytes = ci.getScriptDest(mock_script)
|
||||||
|
else:
|
||||||
|
mock_txo_script: bytes = self.getMockScriptScriptPubkey(ci)
|
||||||
|
real_txo_script: bytes = ci.getScriptDest(script)
|
||||||
|
|
||||||
found: int = 0
|
found: int = 0
|
||||||
ctx = ci.loadTx(mock_tx, allow_witness=False)
|
ctx = ci.loadTx(mock_tx, allow_witness=False)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
- Fixed feerate from other chain displayed for reversed swaps.
|
- Fixed feerate from other chain displayed for reversed swaps.
|
||||||
- Added warning text for fee above 1.2 x local estimate.
|
- Added warning text for fee above 1.2 x local estimate.
|
||||||
- Added subfee bid option.
|
- Added subfee bid option.
|
||||||
|
- Increase DCR fee estimate by 1 byte.
|
||||||
|
|
||||||
|
|
||||||
0.14.5
|
0.14.5
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
@@ -163,7 +162,7 @@ def prepare_balance(
|
|||||||
post_json["type_to"] = type_to
|
post_json["type_to"] = type_to
|
||||||
json_rv = read_json_api(
|
json_rv = read_json_api(
|
||||||
port_take_from_node,
|
port_take_from_node,
|
||||||
"wallets/{}/withdraw".format(coin_ticker.lower()),
|
f"wallets/{coin_ticker.lower()}/withdraw",
|
||||||
post_json,
|
post_json,
|
||||||
)
|
)
|
||||||
assert len(json_rv["txid"]) == 64
|
assert len(json_rv["txid"]) == 64
|
||||||
@@ -236,11 +235,17 @@ def wait_for_bid(
|
|||||||
)
|
)
|
||||||
if isinstance(state, (list, tuple)):
|
if isinstance(state, (list, tuple)):
|
||||||
if bid[5] in state:
|
if bid[5] in state:
|
||||||
|
swap_client.log.debug(
|
||||||
|
f"TEST: wait_for_bid found {bid_id.hex()}: Bid state {bid[5]}, target {state}."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
elif state is not None and state != bid[5]:
|
elif state is not None and state != bid[5]:
|
||||||
continue
|
continue
|
||||||
|
swap_client.log.debug(
|
||||||
|
f"TEST: wait_for_bid found {bid_id.hex()}: Bid state {bid[5]}, target {state}."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if i > 0 and i % 10 == 0:
|
if i > 0 and i % 10 == 0:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
|
|||||||
@@ -175,6 +175,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",
|
||||||
},
|
},
|
||||||
"dash": {
|
"dash": {
|
||||||
"connection_type": "rpc",
|
"connection_type": "rpc",
|
||||||
@@ -184,6 +185,7 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey):
|
|||||||
"bindir": DASH_BINDIR,
|
"bindir": DASH_BINDIR,
|
||||||
"use_csv": True,
|
"use_csv": True,
|
||||||
"use_segwit": False,
|
"use_segwit": False,
|
||||||
|
"wallet_name": "bsx_wallet",
|
||||||
},
|
},
|
||||||
"bitcoin": {
|
"bitcoin": {
|
||||||
"connection_type": "rpc",
|
"connection_type": "rpc",
|
||||||
@@ -192,6 +194,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,
|
||||||
@@ -285,7 +288,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(Test, cls).setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
k = PrivateKey()
|
k = PrivateKey()
|
||||||
cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret)
|
cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret)
|
||||||
@@ -409,15 +412,15 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
waitForRPC(dashRpc, delay_event, rpc_command="getblockchaininfo")
|
waitForRPC(dashRpc, delay_event, rpc_command="getblockchaininfo")
|
||||||
if len(dashRpc("listwallets")) < 1:
|
if len(dashRpc("listwallets")) < 1:
|
||||||
dashRpc("createwallet wbsx_wallet")
|
dashRpc("createwallet bsx_wallet")
|
||||||
|
|
||||||
sc.start()
|
sc.start()
|
||||||
|
|
||||||
waitForRPC(dashRpc, delay_event)
|
waitForRPC(dashRpc, delay_event)
|
||||||
num_blocks = 500
|
num_blocks = 500
|
||||||
logging.info("Mining %d dash blocks", num_blocks)
|
logging.info(f"Mining {num_blocks} dash blocks")
|
||||||
cls.dash_addr = dashRpc("getnewaddress mining_addr")
|
cls.dash_addr = dashRpc("getnewaddress mining_addr")
|
||||||
dashRpc("generatetoaddress {} {}".format(num_blocks, cls.dash_addr))
|
dashRpc(f"generatetoaddress {num_blocks} {cls.dash_addr}")
|
||||||
|
|
||||||
ro = dashRpc("getblockchaininfo")
|
ro = dashRpc("getblockchaininfo")
|
||||||
try:
|
try:
|
||||||
@@ -431,8 +434,8 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
waitForRPC(btcRpc, delay_event)
|
waitForRPC(btcRpc, delay_event)
|
||||||
cls.btc_addr = btcRpc("getnewaddress mining_addr bech32")
|
cls.btc_addr = btcRpc("getnewaddress mining_addr bech32")
|
||||||
logging.info("Mining %d Bitcoin blocks to %s", num_blocks, cls.btc_addr)
|
logging.info(f"Mining {num_blocks} Bitcoin blocks to {cls.btc_addr}")
|
||||||
btcRpc("generatetoaddress {} {}".format(num_blocks, cls.btc_addr))
|
btcRpc(f"generatetoaddress {num_blocks} {cls.btc_addr}")
|
||||||
|
|
||||||
ro = btcRpc("getblockchaininfo")
|
ro = btcRpc("getblockchaininfo")
|
||||||
checkForks(ro)
|
checkForks(ro)
|
||||||
@@ -449,7 +452,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
# Wait for height, or sequencelock is thrown off by genesis blocktime
|
# Wait for height, or sequencelock is thrown off by genesis blocktime
|
||||||
num_blocks = 3
|
num_blocks = 3
|
||||||
logging.info("Waiting for Particl chain height %d", num_blocks)
|
logging.info(f"Waiting for Particl chain height {num_blocks}")
|
||||||
for i in range(60):
|
for i in range(60):
|
||||||
particl_blocks = cls.swap_clients[0].callrpc("getblockcount")
|
particl_blocks = cls.swap_clients[0].callrpc("getblockcount")
|
||||||
print("particl_blocks", particl_blocks)
|
print("particl_blocks", particl_blocks)
|
||||||
@@ -473,7 +476,7 @@ class Test(unittest.TestCase):
|
|||||||
cls.swap_clients.clear()
|
cls.swap_clients.clear()
|
||||||
cls.daemons.clear()
|
cls.daemons.clear()
|
||||||
|
|
||||||
super(Test, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
|
|
||||||
def test_02_part_dash(self):
|
def test_02_part_dash(self):
|
||||||
logging.info("---------- Test PART to DASH")
|
logging.info("---------- Test PART to DASH")
|
||||||
@@ -683,9 +686,9 @@ class Test(unittest.TestCase):
|
|||||||
offer_id = swap_clients[0].postOffer(
|
offer_id = swap_clients[0].postOffer(
|
||||||
Coins.DASH,
|
Coins.DASH,
|
||||||
Coins.BTC,
|
Coins.BTC,
|
||||||
0.001 * COIN,
|
0.01 * COIN,
|
||||||
1.0 * COIN,
|
1.0 * COIN,
|
||||||
0.001 * COIN,
|
0.01 * COIN,
|
||||||
SwapTypes.SELLER_FIRST,
|
SwapTypes.SELLER_FIRST,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -709,7 +712,7 @@ class Test(unittest.TestCase):
|
|||||||
del swap_clients[0].getChainClientSettings(Coins.DASH)["override_feerate"]
|
del swap_clients[0].getChainClientSettings(Coins.DASH)["override_feerate"]
|
||||||
|
|
||||||
def test_08_wallet(self):
|
def test_08_wallet(self):
|
||||||
logging.info("---------- Test {} wallet".format(self.test_coin_from.name))
|
logging.info(f"---------- Test {self.test_coin_from.name} wallet")
|
||||||
|
|
||||||
logging.info("Test withdrawal")
|
logging.info("Test withdrawal")
|
||||||
addr = dashRpc('getnewaddress "Withdrawal test"')
|
addr = dashRpc('getnewaddress "Withdrawal test"')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -75,24 +75,6 @@ def make_rpc_func(node_id, base_rpc_port):
|
|||||||
return rpc_func
|
return rpc_func
|
||||||
|
|
||||||
|
|
||||||
def wait_for_dcr_height(http_port, num_blocks=3):
|
|
||||||
logging.info("Waiting for DCR chain height %d", num_blocks)
|
|
||||||
for i in range(60):
|
|
||||||
if test_delay_event.is_set():
|
|
||||||
raise ValueError("Test stopped.")
|
|
||||||
try:
|
|
||||||
wallet = read_json_api(http_port, "wallets/dcr")
|
|
||||||
decred_blocks = wallet["blocks"]
|
|
||||||
print("decred_blocks", decred_blocks)
|
|
||||||
if decred_blocks >= num_blocks:
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
print("Error reading wallets", str(e))
|
|
||||||
|
|
||||||
test_delay_event.wait(1)
|
|
||||||
raise ValueError(f"wait_for_decred_blocks failed http_port: {http_port}")
|
|
||||||
|
|
||||||
|
|
||||||
def run_test_success_path(self, coin_from: Coins, coin_to: Coins):
|
def run_test_success_path(self, coin_from: Coins, coin_to: Coins):
|
||||||
logging.info(f"---------- Test {coin_from.name} to {coin_to.name}")
|
logging.info(f"---------- Test {coin_from.name} to {coin_to.name}")
|
||||||
|
|
||||||
@@ -765,14 +747,14 @@ class Test(BaseTest):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
logging.info("Finalising Decred Test")
|
logging.info("Finalising Decred Test")
|
||||||
super(Test, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
|
|
||||||
stopDaemons(cls.dcr_daemons)
|
stopDaemons(cls.dcr_daemons)
|
||||||
cls.dcr_daemons.clear()
|
cls.dcr_daemons.clear()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coins_loop(cls):
|
def coins_loop(cls):
|
||||||
super(Test, cls).coins_loop()
|
super().coins_loop()
|
||||||
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
||||||
|
|
||||||
num_passed: int = 0
|
num_passed: int = 0
|
||||||
@@ -878,15 +860,16 @@ class Test(BaseTest):
|
|||||||
"use_csv": True,
|
"use_csv": True,
|
||||||
"use_segwit": True,
|
"use_segwit": True,
|
||||||
"blocks_confirmed": 1,
|
"blocks_confirmed": 1,
|
||||||
|
"min_relay_fee": 0.00001,
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_0001_decred_address(self):
|
def test_0001_decred_address(self):
|
||||||
logging.info("---------- Test {}".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name}")
|
||||||
|
|
||||||
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
coin_settings = {"rpcport": 0, "rpcauth": "none"}
|
||||||
coin_settings.update(REQUIRED_SETTINGS)
|
coin_settings.update(REQUIRED_SETTINGS)
|
||||||
|
|
||||||
ci = DCRInterface(coin_settings, "mainnet")
|
ci = DCRInterface(coin_settings, "mainnet", self.swap_clients[0])
|
||||||
|
|
||||||
k = ci.getNewRandomKey()
|
k = ci.getNewRandomKey()
|
||||||
K = ci.getPubkey(k)
|
K = ci.getPubkey(k)
|
||||||
@@ -914,7 +897,7 @@ class Test(BaseTest):
|
|||||||
assert hash160(masterpubkey_data) == seed_hash
|
assert hash160(masterpubkey_data) == seed_hash
|
||||||
|
|
||||||
def test_001_segwit(self):
|
def test_001_segwit(self):
|
||||||
logging.info("---------- Test {} segwit".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} segwit")
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci0 = swap_clients[0].ci(self.test_coin)
|
ci0 = swap_clients[0].ci(self.test_coin)
|
||||||
@@ -972,7 +955,7 @@ class Test(BaseTest):
|
|||||||
assert f_decoded["txid"] == ctx.TxHash().hex()
|
assert f_decoded["txid"] == ctx.TxHash().hex()
|
||||||
|
|
||||||
def test_003_signature_hash(self):
|
def test_003_signature_hash(self):
|
||||||
logging.info("---------- Test {} signature_hash".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} signature_hash")
|
||||||
# Test that signing a transaction manually produces the same result when signed with the wallet
|
# Test that signing a transaction manually produces the same result when signed with the wallet
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
@@ -1047,7 +1030,7 @@ class Test(BaseTest):
|
|||||||
assert len(sent_txid) == 64
|
assert len(sent_txid) == 64
|
||||||
|
|
||||||
def test_004_csv(self):
|
def test_004_csv(self):
|
||||||
logging.info("---------- Test {} csv".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} csv")
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci0 = swap_clients[0].ci(self.test_coin)
|
ci0 = swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -1161,7 +1144,7 @@ class Test(BaseTest):
|
|||||||
assert sent_spend_txid is not None
|
assert sent_spend_txid is not None
|
||||||
|
|
||||||
def test_005_watchonly(self):
|
def test_005_watchonly(self):
|
||||||
logging.info("---------- Test {} watchonly".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} watchonly")
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci0 = swap_clients[0].ci(self.test_coin)
|
ci0 = swap_clients[0].ci(self.test_coin)
|
||||||
@@ -1261,7 +1244,7 @@ class Test(BaseTest):
|
|||||||
assert found_txid is not None
|
assert found_txid is not None
|
||||||
|
|
||||||
def test_008_gettxout(self):
|
def test_008_gettxout(self):
|
||||||
logging.info("---------- Test {} gettxout".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} gettxout")
|
||||||
|
|
||||||
ci0 = self.swap_clients[0].ci(self.test_coin)
|
ci0 = self.swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -1373,7 +1356,7 @@ class Test(BaseTest):
|
|||||||
assert amount_proved >= require_amount
|
assert amount_proved >= require_amount
|
||||||
|
|
||||||
def test_009_wallet_encryption(self):
|
def test_009_wallet_encryption(self):
|
||||||
logging.info("---------- Test {} wallet encryption".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} wallet encryption")
|
||||||
|
|
||||||
for coin in ("part", "dcr", "xmr"):
|
for coin in ("part", "dcr", "xmr"):
|
||||||
jsw = read_json_api(1800, f"wallets/{coin}")
|
jsw = read_json_api(1800, f"wallets/{coin}")
|
||||||
@@ -1412,7 +1395,7 @@ class Test(BaseTest):
|
|||||||
assert jsw["locked"] is False
|
assert jsw["locked"] is False
|
||||||
|
|
||||||
def test_010_txn_size(self):
|
def test_010_txn_size(self):
|
||||||
logging.info("---------- Test {} txn size".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} txn size")
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci = swap_clients[0].ci(self.test_coin)
|
ci = swap_clients[0].ci(self.test_coin)
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ class Test(TestFunctions):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepareExtraCoins(cls):
|
def prepareExtraCoins(cls):
|
||||||
|
super().prepareExtraCoins()
|
||||||
if cls.restore_instance:
|
if cls.restore_instance:
|
||||||
void_block_rewards_pubkey = cls.getRandomPubkey()
|
void_block_rewards_pubkey = cls.getRandomPubkey()
|
||||||
cls.doge_addr = (
|
cls.doge_addr = (
|
||||||
@@ -232,7 +233,7 @@ class Test(TestFunctions):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
logging.info("Finalising DOGE Test")
|
logging.info("Finalising DOGE Test")
|
||||||
super(Test, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
|
|
||||||
stopDaemons(cls.doge_daemons)
|
stopDaemons(cls.doge_daemons)
|
||||||
cls.doge_daemons.clear()
|
cls.doge_daemons.clear()
|
||||||
@@ -251,11 +252,12 @@ class Test(TestFunctions):
|
|||||||
"use_segwit": False,
|
"use_segwit": False,
|
||||||
"blocks_confirmed": 1,
|
"blocks_confirmed": 1,
|
||||||
"min_relay_fee": 0.01, # RECOMMENDED_MIN_TX_FEE
|
"min_relay_fee": 0.01, # RECOMMENDED_MIN_TX_FEE
|
||||||
|
"wallet_name": "bsx_wallet",
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coins_loop(cls):
|
def coins_loop(cls):
|
||||||
super(Test, cls).coins_loop()
|
super().coins_loop()
|
||||||
if cls.pause_chain:
|
if cls.pause_chain:
|
||||||
return
|
return
|
||||||
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# 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 or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ mkdir -p ${TEST_PATH}/bin
|
|||||||
cp -r ~/tmp/basicswap_bin/* ${TEST_PATH}/bin
|
cp -r ~/tmp/basicswap_bin/* ${TEST_PATH}/bin
|
||||||
export PYTHONPATH=$(pwd)
|
export PYTHONPATH=$(pwd)
|
||||||
export TEST_COINS_LIST='bitcoin,dogecoin'
|
export TEST_COINS_LIST='bitcoin,dogecoin'
|
||||||
python tests/basicswap/extended/test_doge.py
|
python tests/basicswap/extended/test_doge_with_prepare.py
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -27,11 +27,9 @@ from tests.basicswap.extended.test_xmr_persistent import (
|
|||||||
BaseTestWithPrepare,
|
BaseTestWithPrepare,
|
||||||
UI_PORT,
|
UI_PORT,
|
||||||
)
|
)
|
||||||
from tests.basicswap.extended.test_scripts import (
|
|
||||||
wait_for_offers,
|
|
||||||
)
|
|
||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import (
|
||||||
read_json_api,
|
read_json_api,
|
||||||
|
wait_for_offers,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -50,11 +48,11 @@ def wait_for_bid(
|
|||||||
|
|
||||||
bid = read_json_api(UI_PORT + node_id, f"bids/{bid_id}")
|
bid = read_json_api(UI_PORT + node_id, f"bids/{bid_id}")
|
||||||
|
|
||||||
if "state" not in bid:
|
if "bid_state" not in bid:
|
||||||
continue
|
continue
|
||||||
if state is None:
|
if state is None:
|
||||||
return
|
return
|
||||||
if bid["state"].lower() == state.lower():
|
if bid["bid_state"].lower() == state.lower():
|
||||||
return
|
return
|
||||||
raise ValueError("wait_for_bid failed")
|
raise ValueError("wait_for_bid failed")
|
||||||
|
|
||||||
@@ -101,8 +99,9 @@ def prepare_balance(
|
|||||||
|
|
||||||
|
|
||||||
class DOGETest(BaseTestWithPrepare):
|
class DOGETest(BaseTestWithPrepare):
|
||||||
def test_a(self):
|
__test__ = True
|
||||||
|
|
||||||
|
def test_a(self):
|
||||||
amount_from = 10.0
|
amount_from = 10.0
|
||||||
offer_json = {
|
offer_json = {
|
||||||
"coin_from": "btc",
|
"coin_from": "btc",
|
||||||
@@ -114,10 +113,8 @@ class DOGETest(BaseTestWithPrepare):
|
|||||||
"automation_strat_id": 1,
|
"automation_strat_id": 1,
|
||||||
}
|
}
|
||||||
offer_id = read_json_api(UI_PORT + 0, "offers/new", offer_json)["offer_id"]
|
offer_id = read_json_api(UI_PORT + 0, "offers/new", offer_json)["offer_id"]
|
||||||
logging.debug(f"offer_id {offer_id}")
|
|
||||||
|
|
||||||
prepare_balance(self.delay_event, 1, 0, "DOGE", 1000.0)
|
prepare_balance(self.delay_event, 1, 0, "DOGE", 1000.0)
|
||||||
|
|
||||||
wait_for_offers(self.delay_event, 1, 1, offer_id)
|
wait_for_offers(self.delay_event, 1, 1, offer_id)
|
||||||
|
|
||||||
post_json = {"offer_id": offer_id, "amount_from": amount_from}
|
post_json = {"offer_id": offer_id, "amount_from": amount_from}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ def prepareDataDir(
|
|||||||
fp.write("debug=1\n")
|
fp.write("debug=1\n")
|
||||||
fp.write("debugexclude=libevent\n")
|
fp.write("debugexclude=libevent\n")
|
||||||
|
|
||||||
fp.write("fallbackfee=0.01\n")
|
fp.write("fallbackfee=0.0002\n")
|
||||||
fp.write("acceptnonstdtxn=0\n")
|
fp.write("acceptnonstdtxn=0\n")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import unittest
|
|||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import (
|
||||||
read_json_api,
|
read_json_api,
|
||||||
waitForServer,
|
waitForServer,
|
||||||
|
UI_PORT,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -37,9 +38,6 @@ if not len(logger.handlers):
|
|||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
|
|
||||||
PORT_OFS = int(os.getenv("PORT_OFS", 1))
|
|
||||||
UI_PORT = 12700 + PORT_OFS
|
|
||||||
|
|
||||||
ELECTRUM_PATH = os.getenv("ELECTRUM_PATH")
|
ELECTRUM_PATH = os.getenv("ELECTRUM_PATH")
|
||||||
ELECTRUM_DATADIR = os.getenv("ELECTRUM_DATADIR")
|
ELECTRUM_DATADIR = os.getenv("ELECTRUM_DATADIR")
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
@@ -11,22 +11,14 @@ basicswap]$ python tests/basicswap/extended/test_pivx.py
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import shutil
|
|
||||||
import signal
|
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from coincurve.keys import PrivateKey
|
|
||||||
|
|
||||||
import basicswap.config as cfg
|
import basicswap.config as cfg
|
||||||
from basicswap.basicswap import (
|
from basicswap.basicswap import (
|
||||||
BasicSwap,
|
|
||||||
Coins,
|
Coins,
|
||||||
SwapTypes,
|
SwapTypes,
|
||||||
BidStates,
|
BidStates,
|
||||||
@@ -39,30 +31,27 @@ from basicswap.util import (
|
|||||||
from basicswap.basicswap_util import (
|
from basicswap.basicswap_util import (
|
||||||
TxLockTypes,
|
TxLockTypes,
|
||||||
)
|
)
|
||||||
from basicswap.util.address import (
|
|
||||||
toWIF,
|
|
||||||
)
|
|
||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import (
|
||||||
read_json_api,
|
read_json_api,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common import (
|
from tests.basicswap.common import (
|
||||||
callrpc_cli,
|
callrpc_cli,
|
||||||
checkForks,
|
|
||||||
stopDaemons,
|
stopDaemons,
|
||||||
wait_for_bid,
|
wait_for_bid,
|
||||||
wait_for_offer,
|
wait_for_offer,
|
||||||
wait_for_balance,
|
wait_for_balance,
|
||||||
wait_for_unspent,
|
|
||||||
wait_for_in_progress,
|
wait_for_in_progress,
|
||||||
wait_for_bid_tx_state,
|
wait_for_bid_tx_state,
|
||||||
TEST_HTTP_HOST,
|
|
||||||
TEST_HTTP_PORT,
|
TEST_HTTP_PORT,
|
||||||
BASE_PORT,
|
|
||||||
BASE_RPC_PORT,
|
|
||||||
BASE_ZMQ_PORT,
|
|
||||||
PREFIX_SECRET_KEY_REGTEST,
|
|
||||||
waitForRPC,
|
waitForRPC,
|
||||||
|
make_rpc_func,
|
||||||
)
|
)
|
||||||
|
from tests.basicswap.test_xmr import (
|
||||||
|
BaseTest,
|
||||||
|
test_delay_event as delay_event,
|
||||||
|
callnoderpc,
|
||||||
|
)
|
||||||
|
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
||||||
from basicswap.bin.run import startDaemon
|
from basicswap.bin.run import startDaemon
|
||||||
from basicswap.bin.prepare import downloadPIVXParams
|
from basicswap.bin.prepare import downloadPIVXParams
|
||||||
|
|
||||||
@@ -72,11 +61,6 @@ if not len(logger.handlers):
|
|||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
NUM_NODES = 3
|
NUM_NODES = 3
|
||||||
PIVX_NODE = 3
|
|
||||||
BTC_NODE = 4
|
|
||||||
|
|
||||||
delay_event = threading.Event()
|
|
||||||
stop_test = False
|
|
||||||
|
|
||||||
PIVX_BINDIR = os.path.expanduser(
|
PIVX_BINDIR = os.path.expanduser(
|
||||||
os.getenv("PIVX_BINDIR", os.path.join(cfg.DEFAULT_TEST_BINDIR, "pivx"))
|
os.getenv("PIVX_BINDIR", os.path.join(cfg.DEFAULT_TEST_BINDIR, "pivx"))
|
||||||
@@ -85,345 +69,123 @@ PIVXD = os.getenv("PIVXD", "pivxd" + cfg.bin_suffix)
|
|||||||
PIVX_CLI = os.getenv("PIVX_CLI", "pivx-cli" + cfg.bin_suffix)
|
PIVX_CLI = os.getenv("PIVX_CLI", "pivx-cli" + cfg.bin_suffix)
|
||||||
PIVX_TX = os.getenv("PIVX_TX", "pivx-tx" + cfg.bin_suffix)
|
PIVX_TX = os.getenv("PIVX_TX", "pivx-tx" + cfg.bin_suffix)
|
||||||
|
|
||||||
|
PIVX_BASE_PORT = 34832
|
||||||
def prepareOtherDir(datadir, nodeId, conf_file="pivx.conf"):
|
PIVX_BASE_RPC_PORT = 35832
|
||||||
node_dir = os.path.join(datadir, str(nodeId))
|
PIVX_BASE_ZMQ_PORT = 36832
|
||||||
if not os.path.exists(node_dir):
|
|
||||||
os.makedirs(node_dir)
|
|
||||||
filePath = os.path.join(node_dir, conf_file)
|
|
||||||
|
|
||||||
with open(filePath, "w+") as fp:
|
|
||||||
fp.write("regtest=1\n")
|
|
||||||
fp.write("[regtest]\n")
|
|
||||||
fp.write("port=" + str(BASE_PORT + nodeId) + "\n")
|
|
||||||
fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n")
|
|
||||||
|
|
||||||
fp.write("daemon=0\n")
|
|
||||||
fp.write("printtoconsole=0\n")
|
|
||||||
fp.write("server=1\n")
|
|
||||||
fp.write("discover=0\n")
|
|
||||||
fp.write("listenonion=0\n")
|
|
||||||
fp.write("bind=127.0.0.1\n")
|
|
||||||
fp.write("findpeers=0\n")
|
|
||||||
fp.write("debug=1\n")
|
|
||||||
fp.write("debugexclude=libevent\n")
|
|
||||||
|
|
||||||
fp.write("fallbackfee=0.01\n")
|
|
||||||
fp.write("acceptnonstdtxn=0\n")
|
|
||||||
|
|
||||||
if conf_file == "pivx.conf":
|
|
||||||
params_dir = os.path.join(datadir, "pivx-params")
|
|
||||||
downloadPIVXParams(params_dir)
|
|
||||||
fp.write(f"paramsdir={params_dir}\n")
|
|
||||||
|
|
||||||
if conf_file == "bitcoin.conf":
|
|
||||||
fp.write("wallet=bsx_wallet\n")
|
|
||||||
|
|
||||||
|
|
||||||
def prepareDir(datadir, nodeId, network_key, network_pubkey):
|
def pivxCli(cmd, node_id=0):
|
||||||
node_dir = os.path.join(datadir, str(nodeId))
|
|
||||||
if not os.path.exists(node_dir):
|
|
||||||
os.makedirs(node_dir)
|
|
||||||
filePath = os.path.join(node_dir, "particl.conf")
|
|
||||||
|
|
||||||
with open(filePath, "w+") as fp:
|
|
||||||
fp.write("regtest=1\n")
|
|
||||||
fp.write("[regtest]\n")
|
|
||||||
fp.write("port=" + str(BASE_PORT + nodeId) + "\n")
|
|
||||||
fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n")
|
|
||||||
|
|
||||||
fp.write("daemon=0\n")
|
|
||||||
fp.write("printtoconsole=0\n")
|
|
||||||
fp.write("server=1\n")
|
|
||||||
fp.write("discover=0\n")
|
|
||||||
fp.write("listenonion=0\n")
|
|
||||||
fp.write("bind=127.0.0.1\n")
|
|
||||||
fp.write("findpeers=0\n")
|
|
||||||
fp.write("debug=1\n")
|
|
||||||
fp.write("debugexclude=libevent\n")
|
|
||||||
fp.write("zmqpubsmsg=tcp://127.0.0.1:" + str(BASE_ZMQ_PORT + nodeId) + "\n")
|
|
||||||
fp.write("wallet=bsx_wallet\n")
|
|
||||||
fp.write("fallbackfee=0.01\n")
|
|
||||||
|
|
||||||
fp.write("acceptnonstdtxn=0\n")
|
|
||||||
fp.write("minstakeinterval=5\n")
|
|
||||||
fp.write("smsgsregtestadjust=0\n")
|
|
||||||
|
|
||||||
for i in range(0, NUM_NODES):
|
|
||||||
if nodeId == i:
|
|
||||||
continue
|
|
||||||
fp.write("addnode=127.0.0.1:%d\n" % (BASE_PORT + i))
|
|
||||||
|
|
||||||
if nodeId < 2:
|
|
||||||
fp.write("spentindex=1\n")
|
|
||||||
fp.write("txindex=1\n")
|
|
||||||
|
|
||||||
basicswap_dir = os.path.join(datadir, str(nodeId), "basicswap")
|
|
||||||
if not os.path.exists(basicswap_dir):
|
|
||||||
os.makedirs(basicswap_dir)
|
|
||||||
|
|
||||||
pivxdatadir = os.path.join(datadir, str(PIVX_NODE))
|
|
||||||
btcdatadir = os.path.join(datadir, str(BTC_NODE))
|
|
||||||
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
|
|
||||||
settings = {
|
|
||||||
"debug": True,
|
|
||||||
"zmqhost": "tcp://127.0.0.1",
|
|
||||||
"zmqport": BASE_ZMQ_PORT + nodeId,
|
|
||||||
"htmlhost": TEST_HTTP_HOST,
|
|
||||||
"htmlport": TEST_HTTP_PORT + nodeId,
|
|
||||||
"network_key": network_key,
|
|
||||||
"network_pubkey": network_pubkey,
|
|
||||||
"chainclients": {
|
|
||||||
"particl": {
|
|
||||||
"connection_type": "rpc",
|
|
||||||
"manage_daemon": False,
|
|
||||||
"rpcport": BASE_RPC_PORT + nodeId,
|
|
||||||
"datadir": node_dir,
|
|
||||||
"bindir": cfg.PARTICL_BINDIR,
|
|
||||||
"blocks_confirmed": 2, # Faster testing
|
|
||||||
"wallet_name": "bsx_wallet",
|
|
||||||
},
|
|
||||||
"pivx": {
|
|
||||||
"connection_type": "rpc",
|
|
||||||
"manage_daemon": False,
|
|
||||||
"rpcport": BASE_RPC_PORT + PIVX_NODE,
|
|
||||||
"datadir": pivxdatadir,
|
|
||||||
"bindir": PIVX_BINDIR,
|
|
||||||
"use_csv": False,
|
|
||||||
"use_segwit": False,
|
|
||||||
"wallet_name": "",
|
|
||||||
},
|
|
||||||
"bitcoin": {
|
|
||||||
"connection_type": "rpc",
|
|
||||||
"manage_daemon": False,
|
|
||||||
"rpcport": BASE_RPC_PORT + BTC_NODE,
|
|
||||||
"datadir": btcdatadir,
|
|
||||||
"bindir": cfg.BITCOIN_BINDIR,
|
|
||||||
"use_segwit": True,
|
|
||||||
"wallet_name": "bsx_wallet",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"check_progress_seconds": 2,
|
|
||||||
"check_watched_seconds": 4,
|
|
||||||
"check_expired_seconds": 60,
|
|
||||||
"check_events_seconds": 1,
|
|
||||||
"check_xmr_swaps_seconds": 1,
|
|
||||||
"min_delay_event": 1,
|
|
||||||
"max_delay_event": 3,
|
|
||||||
"min_delay_event_short": 1,
|
|
||||||
"max_delay_event_short": 3,
|
|
||||||
"min_delay_retry": 2,
|
|
||||||
"max_delay_retry": 10,
|
|
||||||
"restrict_unknown_seed_wallets": False,
|
|
||||||
"check_updates": False,
|
|
||||||
}
|
|
||||||
with open(settings_path, "w") as fp:
|
|
||||||
json.dump(settings, fp, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
def partRpc(cmd, node_id=0):
|
|
||||||
return callrpc_cli(
|
|
||||||
cfg.PARTICL_BINDIR,
|
|
||||||
os.path.join(cfg.TEST_DATADIRS, str(node_id)),
|
|
||||||
"regtest",
|
|
||||||
cmd,
|
|
||||||
cfg.PARTICL_CLI,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def btcRpc(cmd):
|
|
||||||
return callrpc_cli(
|
|
||||||
cfg.BITCOIN_BINDIR,
|
|
||||||
os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)),
|
|
||||||
"regtest",
|
|
||||||
cmd,
|
|
||||||
cfg.BITCOIN_CLI,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def pivxRpc(cmd):
|
|
||||||
return callrpc_cli(
|
return callrpc_cli(
|
||||||
PIVX_BINDIR,
|
PIVX_BINDIR,
|
||||||
os.path.join(cfg.TEST_DATADIRS, str(PIVX_NODE)),
|
os.path.join(cfg.TEST_DATADIRS, "pivx_" + str(node_id)),
|
||||||
"regtest",
|
"regtest",
|
||||||
cmd,
|
cmd,
|
||||||
PIVX_CLI,
|
PIVX_CLI,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def prepareDataDir(
|
||||||
global stop_test
|
datadir, node_id, conf_file, dir_prefix, base_p2p_port, base_rpc_port, num_nodes=3
|
||||||
os.write(sys.stdout.fileno(), f"Signal {sig} detected.\n".encode("utf-8"))
|
):
|
||||||
stop_test = True
|
node_dir = os.path.join(datadir, dir_prefix + str(node_id))
|
||||||
delay_event.set()
|
if not os.path.exists(node_dir):
|
||||||
|
os.makedirs(node_dir)
|
||||||
|
cfg_file_path = os.path.join(node_dir, conf_file)
|
||||||
|
if os.path.exists(cfg_file_path):
|
||||||
|
return
|
||||||
|
with open(cfg_file_path, "w+") as fp:
|
||||||
|
fp.write("regtest=1\n")
|
||||||
|
fp.write("[regtest]\n")
|
||||||
|
fp.write("port=" + str(base_p2p_port + node_id) + "\n")
|
||||||
|
fp.write("rpcport=" + str(base_rpc_port + node_id) + "\n")
|
||||||
|
|
||||||
|
salt = generate_salt(16)
|
||||||
|
fp.write(
|
||||||
|
"rpcauth={}:{}${}\n".format(
|
||||||
|
"test" + str(node_id),
|
||||||
|
salt,
|
||||||
|
password_to_hmac(salt, "test_pass" + str(node_id)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fp.write("daemon=0\n")
|
||||||
|
fp.write("printtoconsole=0\n")
|
||||||
|
fp.write("server=1\n")
|
||||||
|
fp.write("discover=0\n")
|
||||||
|
fp.write("listenonion=0\n")
|
||||||
|
fp.write("bind=127.0.0.1\n")
|
||||||
|
fp.write("findpeers=0\n")
|
||||||
|
fp.write("debug=1\n")
|
||||||
|
fp.write("debugexclude=libevent\n")
|
||||||
|
|
||||||
|
fp.write("fallbackfee=0.01\n")
|
||||||
|
fp.write("acceptnonstdtxn=0\n")
|
||||||
|
|
||||||
|
params_dir = os.path.join(datadir, "pivx-params")
|
||||||
|
downloadPIVXParams(params_dir)
|
||||||
|
fp.write(f"paramsdir={params_dir}\n")
|
||||||
|
|
||||||
|
for i in range(0, num_nodes):
|
||||||
|
if node_id == i:
|
||||||
|
continue
|
||||||
|
fp.write("addnode=127.0.0.1:{}\n".format(base_p2p_port + i))
|
||||||
|
|
||||||
|
return node_dir
|
||||||
|
|
||||||
|
|
||||||
def run_coins_loop(cls):
|
class Test(BaseTest):
|
||||||
while not stop_test:
|
__test__ = True
|
||||||
try:
|
|
||||||
pivxRpc("generatetoaddress 1 {}".format(cls.pivx_addr))
|
|
||||||
btcRpc("generatetoaddress 1 {}".format(cls.btc_addr))
|
|
||||||
except Exception as e:
|
|
||||||
logging.warning("run_coins_loop " + str(e))
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
|
|
||||||
def run_loop(self):
|
|
||||||
while not stop_test:
|
|
||||||
for c in self.swap_clients:
|
|
||||||
c.update()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
def make_part_cli_rpc_func(node_id):
|
|
||||||
node_id = node_id
|
|
||||||
|
|
||||||
def rpc_func(method, params=None, wallet=None):
|
|
||||||
cmd = method
|
|
||||||
if params:
|
|
||||||
for p in params:
|
|
||||||
cmd += ' "' + p + '"'
|
|
||||||
return partRpc(cmd, node_id)
|
|
||||||
|
|
||||||
return rpc_func
|
|
||||||
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
|
||||||
test_coin_from = Coins.PIVX
|
test_coin_from = Coins.PIVX
|
||||||
|
pivx_daemons = []
|
||||||
|
pivx_addr = None
|
||||||
|
start_ltc_nodes = False
|
||||||
|
start_xmr_nodes = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def prepareExtraDataDir(cls, i):
|
||||||
super(Test, cls).setUpClass()
|
extra_opts = []
|
||||||
|
if not cls.restore_instance:
|
||||||
k = PrivateKey()
|
prepareDataDir(
|
||||||
cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret)
|
cfg.TEST_DATADIRS,
|
||||||
cls.network_pubkey = k.public_key.format().hex()
|
i,
|
||||||
|
"pivx.conf",
|
||||||
if os.path.isdir(cfg.TEST_DATADIRS):
|
"pivx_",
|
||||||
logging.info("Removing " + cfg.TEST_DATADIRS)
|
base_p2p_port=PIVX_BASE_PORT,
|
||||||
for name in os.listdir(cfg.TEST_DATADIRS):
|
base_rpc_port=PIVX_BASE_RPC_PORT,
|
||||||
if name == "pivx-params":
|
|
||||||
continue
|
|
||||||
fullpath = os.path.join(cfg.TEST_DATADIRS, name)
|
|
||||||
if os.path.isdir(fullpath):
|
|
||||||
shutil.rmtree(fullpath)
|
|
||||||
else:
|
|
||||||
os.remove(fullpath)
|
|
||||||
|
|
||||||
for i in range(NUM_NODES):
|
|
||||||
prepareDir(cfg.TEST_DATADIRS, i, cls.network_key, cls.network_pubkey)
|
|
||||||
|
|
||||||
prepareOtherDir(cfg.TEST_DATADIRS, PIVX_NODE)
|
|
||||||
prepareOtherDir(cfg.TEST_DATADIRS, BTC_NODE, "bitcoin.conf")
|
|
||||||
|
|
||||||
cls.daemons = []
|
|
||||||
cls.swap_clients = []
|
|
||||||
|
|
||||||
btc_data_dir = os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE))
|
|
||||||
if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, "bitcoin-wallet")):
|
|
||||||
try:
|
|
||||||
callrpc_cli(
|
|
||||||
cfg.BITCOIN_BINDIR,
|
|
||||||
btc_data_dir,
|
|
||||||
"regtest",
|
|
||||||
"-wallet=bsx_wallet -legacy create",
|
|
||||||
"bitcoin-wallet",
|
|
||||||
)
|
)
|
||||||
except Exception:
|
cls.pivx_daemons.append(
|
||||||
callrpc_cli(
|
|
||||||
cfg.BITCOIN_BINDIR,
|
|
||||||
btc_data_dir,
|
|
||||||
"regtest",
|
|
||||||
"-wallet=bsx_wallet create",
|
|
||||||
"bitcoin-wallet",
|
|
||||||
)
|
|
||||||
cls.daemons.append(startDaemon(btc_data_dir, cfg.BITCOIN_BINDIR, cfg.BITCOIND))
|
|
||||||
logging.info("Started %s %d", cfg.BITCOIND, cls.daemons[-1].handle.pid)
|
|
||||||
cls.daemons.append(
|
|
||||||
startDaemon(
|
startDaemon(
|
||||||
os.path.join(cfg.TEST_DATADIRS, str(PIVX_NODE)), PIVX_BINDIR, PIVXD
|
os.path.join(cfg.TEST_DATADIRS, "pivx_" + str(i)),
|
||||||
|
PIVX_BINDIR,
|
||||||
|
PIVXD,
|
||||||
|
opts=extra_opts,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logging.info("Started %s %d", PIVXD, cls.daemons[-1].handle.pid)
|
logging.info("Started %s %d", PIVXD, cls.pivx_daemons[-1].handle.pid)
|
||||||
|
|
||||||
for i in range(NUM_NODES):
|
waitForRPC(make_rpc_func(i, base_rpc_port=PIVX_BASE_RPC_PORT), delay_event)
|
||||||
data_dir = os.path.join(cfg.TEST_DATADIRS, str(i))
|
|
||||||
if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, "particl-wallet")):
|
|
||||||
try:
|
|
||||||
callrpc_cli(
|
|
||||||
cfg.PARTICL_BINDIR,
|
|
||||||
data_dir,
|
|
||||||
"regtest",
|
|
||||||
"-wallet=bsx_wallet -legacy create",
|
|
||||||
"particl-wallet",
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
callrpc_cli(
|
|
||||||
cfg.PARTICL_BINDIR,
|
|
||||||
data_dir,
|
|
||||||
"regtest",
|
|
||||||
"-wallet=bsx_wallet create",
|
|
||||||
"particl-wallet",
|
|
||||||
)
|
|
||||||
cls.daemons.append(startDaemon(data_dir, cfg.PARTICL_BINDIR, cfg.PARTICLD))
|
|
||||||
logging.info("Started %s %d", cfg.PARTICLD, cls.daemons[-1].handle.pid)
|
|
||||||
|
|
||||||
for i in range(NUM_NODES):
|
@classmethod
|
||||||
rpc = make_part_cli_rpc_func(i)
|
def addPIDInfo(cls, sc, i):
|
||||||
waitForRPC(rpc, delay_event)
|
sc.setDaemonPID(Coins.PIVX, cls.pivx_daemons[i].handle.pid)
|
||||||
if i == 0:
|
|
||||||
rpc(
|
@classmethod
|
||||||
"extkeyimportmaster",
|
def prepareExtraCoins(cls):
|
||||||
[
|
|
||||||
"abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb"
|
if cls.restore_instance:
|
||||||
],
|
void_block_rewards_pubkey = cls.getRandomPubkey()
|
||||||
|
cls.pivx_addr = (
|
||||||
|
cls.swap_clients[0]
|
||||||
|
.ci(Coins.PIVX)
|
||||||
|
.pubkey_to_address(void_block_rewards_pubkey)
|
||||||
)
|
)
|
||||||
elif i == 1:
|
|
||||||
rpc(
|
|
||||||
"extkeyimportmaster",
|
|
||||||
[
|
|
||||||
"pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic",
|
|
||||||
"",
|
|
||||||
"true",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
rpc("getnewextaddress", ["lblExtTest"])
|
|
||||||
rpc("rescanblockchain")
|
|
||||||
else:
|
else:
|
||||||
rpc("extkeyimportmaster", [rpc("mnemonic", ["new"])["master"]])
|
|
||||||
rpc(
|
|
||||||
"walletsettings",
|
|
||||||
[
|
|
||||||
"stakingoptions",
|
|
||||||
json.dumps(
|
|
||||||
{"stakecombinethreshold": 100, "stakesplitthreshold": 200}
|
|
||||||
).replace('"', '\\"'),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
rpc("reservebalance", ["false"])
|
|
||||||
|
|
||||||
basicswap_dir = os.path.join(
|
|
||||||
os.path.join(cfg.TEST_DATADIRS, str(i)), "basicswap"
|
|
||||||
)
|
|
||||||
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
|
|
||||||
with open(settings_path) as fs:
|
|
||||||
settings = json.load(fs)
|
|
||||||
sc = BasicSwap(
|
|
||||||
basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i)
|
|
||||||
)
|
|
||||||
cls.swap_clients.append(sc)
|
|
||||||
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
|
|
||||||
sc.setDaemonPID(Coins.PIVX, cls.daemons[1].handle.pid)
|
|
||||||
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
|
|
||||||
sc.start()
|
|
||||||
|
|
||||||
waitForRPC(pivxRpc, delay_event)
|
|
||||||
num_blocks = 1352 # CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height 1351.
|
num_blocks = 1352 # CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height 1351.
|
||||||
logging.info("Mining %d pivx blocks", num_blocks)
|
logging.info(f"Mining {num_blocks} pivx blocks")
|
||||||
cls.pivx_addr = pivxRpc("getnewaddress mining_addr")
|
cls.pivx_addr = pivxCli("getnewaddress mining_addr")
|
||||||
pivxRpc("generatetoaddress {} {}".format(num_blocks, cls.pivx_addr))
|
pivxCli(f"generatetoaddress {num_blocks} {cls.pivx_addr}")
|
||||||
|
|
||||||
ro = pivxRpc("getblockchaininfo")
|
ro = pivxCli("getblockchaininfo")
|
||||||
try:
|
try:
|
||||||
assert ro["bip9_softforks"]["csv"]["status"] == "active"
|
assert ro["bip9_softforks"]["csv"]["status"] == "active"
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -433,47 +195,47 @@ class Test(unittest.TestCase):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logging.info("pivx: segwit is not active")
|
logging.info("pivx: segwit is not active")
|
||||||
|
|
||||||
waitForRPC(btcRpc, delay_event)
|
|
||||||
cls.btc_addr = btcRpc("getnewaddress mining_addr bech32")
|
|
||||||
logging.info("Mining %d Bitcoin blocks to %s", num_blocks, cls.btc_addr)
|
|
||||||
btcRpc("generatetoaddress {} {}".format(num_blocks, cls.btc_addr))
|
|
||||||
|
|
||||||
ro = btcRpc("getblockchaininfo")
|
|
||||||
checkForks(ro)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
|
|
||||||
cls.update_thread.start()
|
|
||||||
|
|
||||||
cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
|
|
||||||
cls.coins_update_thread.start()
|
|
||||||
|
|
||||||
# Wait for height, or sequencelock is thrown off by genesis blocktime
|
|
||||||
num_blocks = 3
|
|
||||||
logging.info("Waiting for Particl chain height %d", num_blocks)
|
|
||||||
for i in range(60):
|
|
||||||
particl_blocks = cls.swap_clients[0].callrpc("getblockcount")
|
|
||||||
print("particl_blocks", particl_blocks)
|
|
||||||
if particl_blocks >= num_blocks:
|
|
||||||
break
|
|
||||||
delay_event.wait(1)
|
|
||||||
assert particl_blocks >= num_blocks
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
global stop_test
|
logging.info("Finalising PIVX Test")
|
||||||
logging.info("Finalising")
|
super().tearDownClass()
|
||||||
stop_test = True
|
|
||||||
cls.update_thread.join()
|
|
||||||
cls.coins_update_thread.join()
|
|
||||||
for c in cls.swap_clients:
|
|
||||||
c.finalise()
|
|
||||||
|
|
||||||
stopDaemons(cls.daemons)
|
stopDaemons(cls.pivx_daemons)
|
||||||
cls.swap_clients.clear()
|
cls.pivx_daemons.clear()
|
||||||
cls.daemons.clear()
|
|
||||||
|
|
||||||
super(Test, cls).tearDownClass()
|
@classmethod
|
||||||
|
def addCoinSettings(cls, settings, datadir, node_id):
|
||||||
|
settings["chainclients"]["pivx"] = {
|
||||||
|
"connection_type": "rpc",
|
||||||
|
"manage_daemon": False,
|
||||||
|
"rpcport": PIVX_BASE_RPC_PORT + node_id,
|
||||||
|
"rpcuser": "test" + str(node_id),
|
||||||
|
"rpcpassword": "test_pass" + str(node_id),
|
||||||
|
"datadir": os.path.join(datadir, "pivx_" + str(node_id)),
|
||||||
|
"bindir": PIVX_BINDIR,
|
||||||
|
"use_csv": False,
|
||||||
|
"use_segwit": False,
|
||||||
|
"wallet_name": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def coins_loop(cls):
|
||||||
|
super().coins_loop()
|
||||||
|
callnoderpc(
|
||||||
|
0, "generatetoaddress", [1, cls.pivx_addr], base_rpc_port=PIVX_BASE_RPC_PORT
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def prepareBalances(cls):
|
||||||
|
super().prepareBalances()
|
||||||
|
|
||||||
|
cls.prepare_balance(
|
||||||
|
cls,
|
||||||
|
Coins.PIVX,
|
||||||
|
10000.0,
|
||||||
|
1801,
|
||||||
|
1800,
|
||||||
|
)
|
||||||
|
|
||||||
def test_02_part_pivx(self):
|
def test_02_part_pivx(self):
|
||||||
logging.info("---------- Test PART to PIVX")
|
logging.info("---------- Test PART to PIVX")
|
||||||
@@ -500,7 +262,7 @@ class Test(unittest.TestCase):
|
|||||||
wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True)
|
wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True)
|
||||||
|
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60
|
delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=80
|
||||||
)
|
)
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
delay_event,
|
delay_event,
|
||||||
@@ -508,7 +270,7 @@ class Test(unittest.TestCase):
|
|||||||
bid_id,
|
bid_id,
|
||||||
BidStates.SWAP_COMPLETED,
|
BidStates.SWAP_COMPLETED,
|
||||||
sent=True,
|
sent=True,
|
||||||
wait_for=60,
|
wait_for=80,
|
||||||
)
|
)
|
||||||
|
|
||||||
js_0 = read_json_api(1800)
|
js_0 = read_json_api(1800)
|
||||||
@@ -548,7 +310,7 @@ class Test(unittest.TestCase):
|
|||||||
wait_for=60,
|
wait_for=60,
|
||||||
)
|
)
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60
|
delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=80
|
||||||
)
|
)
|
||||||
|
|
||||||
js_0 = read_json_api(1800)
|
js_0 = read_json_api(1800)
|
||||||
@@ -580,7 +342,7 @@ class Test(unittest.TestCase):
|
|||||||
wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True)
|
wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True)
|
||||||
|
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60
|
delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=80
|
||||||
)
|
)
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
delay_event,
|
delay_event,
|
||||||
@@ -717,7 +479,7 @@ class Test(unittest.TestCase):
|
|||||||
logging.info("---------- Test {} wallet".format(self.test_coin_from.name))
|
logging.info("---------- Test {} wallet".format(self.test_coin_from.name))
|
||||||
|
|
||||||
logging.info("Test withdrawal")
|
logging.info("Test withdrawal")
|
||||||
addr = pivxRpc('getnewaddress "Withdrawal test"')
|
addr = pivxCli('getnewaddress "Withdrawal test"')
|
||||||
wallets = read_json_api(TEST_HTTP_PORT + 0, "wallets")
|
wallets = read_json_api(TEST_HTTP_PORT + 0, "wallets")
|
||||||
assert float(wallets[self.test_coin_from.name]["balance"]) > 100
|
assert float(wallets[self.test_coin_from.name]["balance"]) > 100
|
||||||
|
|
||||||
@@ -747,30 +509,30 @@ class Test(unittest.TestCase):
|
|||||||
def test_09_v3_tx(self):
|
def test_09_v3_tx(self):
|
||||||
logging.info("---------- Test PIVX v3 txns")
|
logging.info("---------- Test PIVX v3 txns")
|
||||||
|
|
||||||
generate_addr = pivxRpc('getnewaddress "generate test"')
|
generate_addr = pivxCli('getnewaddress "generate test"')
|
||||||
pivx_addr = pivxRpc('getnewaddress "Sapling test"')
|
pivx_addr = pivxCli('getnewaddress "Sapling test"')
|
||||||
pivx_sapling_addr = pivxRpc('getnewshieldaddress "shield addr"')
|
pivx_sapling_addr = pivxCli('getnewshieldaddress "shield addr"')
|
||||||
|
|
||||||
pivxRpc(f'sendtoaddress "{pivx_addr}" 6.0')
|
pivxCli(f'sendtoaddress "{pivx_addr}" 6.0')
|
||||||
pivxRpc(f'generatetoaddress 1 "{generate_addr}"')
|
pivxCli(f'generatetoaddress 1 "{generate_addr}"')
|
||||||
|
|
||||||
txid = pivxRpc(
|
txid = pivxCli(
|
||||||
'shieldsendmany "{}" "[{{\\"address\\": \\"{}\\", \\"amount\\": 1}}]"'.format(
|
'shieldsendmany "{}" "[{{\\"address\\": \\"{}\\", \\"amount\\": 1}}]"'.format(
|
||||||
pivx_addr, pivx_sapling_addr
|
pivx_addr, pivx_sapling_addr
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
rtx = pivxRpc(f'getrawtransaction "{txid}" true')
|
rtx = pivxCli(f'getrawtransaction "{txid}" true')
|
||||||
assert rtx["version"] == 3
|
assert rtx["version"] == 3
|
||||||
|
|
||||||
block_hash = None
|
block_hash = None
|
||||||
for i in range(15):
|
for i in range(15):
|
||||||
rtx = pivxRpc(f'getrawtransaction "{txid}" true')
|
rtx = pivxCli(f'getrawtransaction "{txid}" true')
|
||||||
if "blockhash" in rtx:
|
if "blockhash" in rtx:
|
||||||
block_hash = rtx["blockhash"]
|
block_hash = rtx["blockhash"]
|
||||||
logging.info(f"Shielded tx confirmed in block {block_hash} after {i}s")
|
logging.info(f"Shielded tx confirmed in block {block_hash} after {i}s")
|
||||||
break
|
break
|
||||||
if i == 5:
|
if i == 5:
|
||||||
pivxRpc(f'generatetoaddress 1 "{generate_addr}"')
|
pivxCli(f'generatetoaddress 1 "{generate_addr}"')
|
||||||
delay_event.wait(1)
|
delay_event.wait(1)
|
||||||
assert block_hash is not None, "Shielded tx was not confirmed"
|
assert block_hash is not None, "Shielded tx was not confirmed"
|
||||||
|
|
||||||
@@ -860,7 +622,6 @@ class Test(unittest.TestCase):
|
|||||||
value_after_subfee = ci_from.make_int(itx_decoded["vout"][n]["value"])
|
value_after_subfee = ci_from.make_int(itx_decoded["vout"][n]["value"])
|
||||||
assert value_after_subfee < swap_value
|
assert value_after_subfee < swap_value
|
||||||
swap_value = value_after_subfee
|
swap_value = value_after_subfee
|
||||||
wait_for_unspent(delay_event, ci_from, swap_value)
|
|
||||||
|
|
||||||
extra_options = {"prefunded_itx": itx}
|
extra_options = {"prefunded_itx": itx}
|
||||||
rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
|
rate_swap = ci_to.make_int(random.uniform(0.2, 10.0), r=1)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2023-2024 tecnovert
|
# Copyright (c) 2023-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@ from tests.basicswap.common import (
|
|||||||
from tests.basicswap.util import (
|
from tests.basicswap.util import (
|
||||||
read_json_api,
|
read_json_api,
|
||||||
waitForServer,
|
waitForServer,
|
||||||
|
wait_for_offers,
|
||||||
|
UI_PORT,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -44,10 +46,6 @@ if not len(logger.handlers):
|
|||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
|
|
||||||
PORT_OFS = int(os.getenv("PORT_OFS", 1))
|
|
||||||
UI_PORT = 12700 + PORT_OFS
|
|
||||||
|
|
||||||
|
|
||||||
class HttpHandler(BaseHTTPRequestHandler):
|
class HttpHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
def js_response(self, url_split, post_string, is_json):
|
def js_response(self, url_split, post_string, is_json):
|
||||||
@@ -131,18 +129,6 @@ def clear_offers(delay_event, node_id) -> None:
|
|||||||
raise ValueError("clear_offers failed")
|
raise ValueError("clear_offers failed")
|
||||||
|
|
||||||
|
|
||||||
def wait_for_offers(delay_event, node_id, num_offers, offer_id=None) -> None:
|
|
||||||
logging.info(f"Waiting for {num_offers} offers on node {node_id}")
|
|
||||||
for i in range(20):
|
|
||||||
delay_event.wait(1)
|
|
||||||
offers = read_json_api(
|
|
||||||
UI_PORT + node_id, "offers" if offer_id is None else f"offers/{offer_id}"
|
|
||||||
)
|
|
||||||
if len(offers) >= num_offers:
|
|
||||||
return
|
|
||||||
raise ValueError("wait_for_offers failed")
|
|
||||||
|
|
||||||
|
|
||||||
def wait_for_bids(delay_event, node_id, num_bids, offer_id=None) -> None:
|
def wait_for_bids(delay_event, node_id, num_bids, offer_id=None) -> None:
|
||||||
logging.info(f"Waiting for {num_bids} bids on node {node_id}")
|
logging.info(f"Waiting for {num_bids} bids on node {node_id}")
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
# 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 time
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from basicswap.basicswap import (
|
from basicswap.basicswap import (
|
||||||
Coins,
|
Coins,
|
||||||
@@ -120,14 +120,14 @@ class Test(BaseTest):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
logging.info("Finalising Wownero Test")
|
logging.info("Finalising Wownero Test")
|
||||||
super(Test, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
|
|
||||||
stopDaemons(cls.wow_daemons)
|
stopDaemons(cls.wow_daemons)
|
||||||
cls.wow_daemons.clear()
|
cls.wow_daemons.clear()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coins_loop(cls):
|
def coins_loop(cls):
|
||||||
super(Test, cls).coins_loop()
|
super().coins_loop()
|
||||||
|
|
||||||
if cls.wow_addr is not None:
|
if cls.wow_addr is not None:
|
||||||
callrpc_xmr(
|
callrpc_xmr(
|
||||||
@@ -162,7 +162,7 @@ class Test(BaseTest):
|
|||||||
startXmrWalletDaemon(node_dir, WOW_BINDIR, WOW_WALLET_RPC, opts=opts)
|
startXmrWalletDaemon(node_dir, WOW_BINDIR, WOW_WALLET_RPC, opts=opts)
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.wow_wallet_auth.append(("test{0}".format(i), "test_pass{0}".format(i)))
|
cls.wow_wallet_auth.append((f"test{i}", f"test_pass{i}"))
|
||||||
|
|
||||||
waitForWOWNode(i, auth=cls.wow_wallet_auth[i])
|
waitForWOWNode(i, auth=cls.wow_wallet_auth[i])
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ from tests.basicswap.util import (
|
|||||||
make_boolean,
|
make_boolean,
|
||||||
read_json_api,
|
read_json_api,
|
||||||
waitForServer,
|
waitForServer,
|
||||||
|
PORT_OFS,
|
||||||
|
UI_PORT,
|
||||||
)
|
)
|
||||||
from tests.basicswap.common_xmr import (
|
from tests.basicswap.common_xmr import (
|
||||||
prepare_nodes,
|
prepare_nodes,
|
||||||
@@ -73,9 +75,6 @@ import basicswap.bin.run as runSystem
|
|||||||
test_path = os.path.expanduser(os.getenv("TEST_PATH", "/tmp/test_persistent"))
|
test_path = os.path.expanduser(os.getenv("TEST_PATH", "/tmp/test_persistent"))
|
||||||
RESET_TEST = make_boolean(os.getenv("RESET_TEST", "true"))
|
RESET_TEST = make_boolean(os.getenv("RESET_TEST", "true"))
|
||||||
|
|
||||||
PORT_OFS = int(os.getenv("PORT_OFS", 1))
|
|
||||||
UI_PORT = 12700 + PORT_OFS
|
|
||||||
|
|
||||||
PARTICL_RPC_PORT_BASE = int(os.getenv("PARTICL_RPC_PORT_BASE", BASE_RPC_PORT))
|
PARTICL_RPC_PORT_BASE = int(os.getenv("PARTICL_RPC_PORT_BASE", BASE_RPC_PORT))
|
||||||
BITCOIN_RPC_PORT_BASE = int(os.getenv("BITCOIN_RPC_PORT_BASE", BTC_BASE_RPC_PORT))
|
BITCOIN_RPC_PORT_BASE = int(os.getenv("BITCOIN_RPC_PORT_BASE", BTC_BASE_RPC_PORT))
|
||||||
LITECOIN_RPC_PORT_BASE = int(os.getenv("LITECOIN_RPC_PORT_BASE", LTC_BASE_RPC_PORT))
|
LITECOIN_RPC_PORT_BASE = int(os.getenv("LITECOIN_RPC_PORT_BASE", LTC_BASE_RPC_PORT))
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020 tecnovert
|
# Copyright (c) 2020 tecnovert
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2023 tecnovert
|
# Copyright (c) 2023 tecnovert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# 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 or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -167,6 +167,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepareExtraCoins(cls):
|
def prepareExtraCoins(cls):
|
||||||
|
super().prepareExtraCoins()
|
||||||
cls.bch_addr = callnoderpc(
|
cls.bch_addr = callnoderpc(
|
||||||
0,
|
0,
|
||||||
"getnewaddress",
|
"getnewaddress",
|
||||||
@@ -197,11 +198,12 @@ class TestBCH(BasicSwapTest):
|
|||||||
"datadir": os.path.join(datadir, "bch_" + str(node_id)),
|
"datadir": os.path.join(datadir, "bch_" + str(node_id)),
|
||||||
"bindir": BITCOINCASH_BINDIR,
|
"bindir": BITCOINCASH_BINDIR,
|
||||||
"use_segwit": False,
|
"use_segwit": False,
|
||||||
|
"wallet_name": "bsx_wallet",
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coins_loop(cls):
|
def coins_loop(cls):
|
||||||
super(TestBCH, cls).coins_loop()
|
super().coins_loop()
|
||||||
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
ci0 = cls.swap_clients[0].ci(cls.test_coin)
|
||||||
try:
|
try:
|
||||||
if cls.bch_addr is not None:
|
if cls.bch_addr is not None:
|
||||||
@@ -212,7 +214,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
logging.info("Finalising Bitcoincash Test")
|
logging.info("Finalising Bitcoincash Test")
|
||||||
super(TestBCH, cls).tearDownClass()
|
super().tearDownClass()
|
||||||
|
|
||||||
stopDaemons(cls.bch_daemons)
|
stopDaemons(cls.bch_daemons)
|
||||||
cls.bch_daemons.clear()
|
cls.bch_daemons.clear()
|
||||||
@@ -224,19 +226,15 @@ class TestBCH(BasicSwapTest):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def test_001_nested_segwit(self):
|
def test_001_nested_segwit(self):
|
||||||
logging.info(
|
logging.info(f"---------- Test {self.test_coin.name} p2sh nested segwit")
|
||||||
"---------- Test {} p2sh nested segwit".format(self.test_coin.name)
|
|
||||||
)
|
|
||||||
logging.info("Skipped")
|
logging.info("Skipped")
|
||||||
|
|
||||||
def test_002_native_segwit(self):
|
def test_002_native_segwit(self):
|
||||||
logging.info(
|
logging.info(f"---------- Test {self.test_coin.name} p2sh native segwit")
|
||||||
"---------- Test {} p2sh native segwit".format(self.test_coin.name)
|
|
||||||
)
|
|
||||||
logging.info("Skipped")
|
logging.info("Skipped")
|
||||||
|
|
||||||
def test_003_cltv(self):
|
def test_003_cltv(self):
|
||||||
logging.info("---------- Test {} cltv".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} cltv")
|
||||||
|
|
||||||
ci = self.swap_clients[0].ci(self.test_coin)
|
ci = self.swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -348,7 +346,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
assert len(tx_wallet["blockhash"]) == 64
|
assert len(tx_wallet["blockhash"]) == 64
|
||||||
|
|
||||||
def test_004_csv(self):
|
def test_004_csv(self):
|
||||||
logging.info("---------- Test {} csv".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} csv")
|
||||||
|
|
||||||
ci = self.swap_clients[0].ci(self.test_coin)
|
ci = self.swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -451,7 +449,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
assert len(tx_wallet["blockhash"]) == 64
|
assert len(tx_wallet["blockhash"]) == 64
|
||||||
|
|
||||||
def test_005_watchonly(self):
|
def test_005_watchonly(self):
|
||||||
logging.info("---------- Test {} watchonly".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} watchonly")
|
||||||
ci = self.swap_clients[0].ci(self.test_coin)
|
ci = self.swap_clients[0].ci(self.test_coin)
|
||||||
ci1 = self.swap_clients[1].ci(self.test_coin)
|
ci1 = self.swap_clients[1].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -482,7 +480,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
super().test_006_getblock_verbosity()
|
super().test_006_getblock_verbosity()
|
||||||
|
|
||||||
def test_007_hdwallet(self):
|
def test_007_hdwallet(self):
|
||||||
logging.info("---------- Test {} hdwallet".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} hdwallet")
|
||||||
|
|
||||||
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
||||||
test_wif = (
|
test_wif = (
|
||||||
@@ -506,10 +504,10 @@ class TestBCH(BasicSwapTest):
|
|||||||
super().test_009_scantxoutset()
|
super().test_009_scantxoutset()
|
||||||
|
|
||||||
def test_010_txn_size(self):
|
def test_010_txn_size(self):
|
||||||
logging.info("---------- Test {} txn_size".format(Coins.BCH))
|
logging.info(f"---------- Test {self.test_coin.name} txn_size")
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci = swap_clients[0].ci(Coins.BCH)
|
ci = swap_clients[0].ci(self.test_coin)
|
||||||
pi = swap_clients[0].pi(SwapTypes.XMR_SWAP)
|
pi = swap_clients[0].pi(SwapTypes.XMR_SWAP)
|
||||||
|
|
||||||
amount: int = ci.make_int(random.uniform(0.1, 2.0), r=1)
|
amount: int = ci.make_int(random.uniform(0.1, 2.0), r=1)
|
||||||
@@ -627,7 +625,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
|
|
||||||
def test_011_p2sh(self):
|
def test_011_p2sh(self):
|
||||||
# Not used in bsx for native-segwit coins
|
# Not used in bsx for native-segwit coins
|
||||||
logging.info("---------- Test {} p2sh".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} p2sh")
|
||||||
|
|
||||||
ci = self.swap_clients[0].ci(self.test_coin)
|
ci = self.swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -717,7 +715,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
|
|
||||||
def test_011_p2sh32(self):
|
def test_011_p2sh32(self):
|
||||||
# Not used in bsx for native-segwit coins
|
# Not used in bsx for native-segwit coins
|
||||||
logging.info("---------- Test {} p2sh32".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} p2sh32")
|
||||||
|
|
||||||
ci = self.swap_clients[0].ci(self.test_coin)
|
ci = self.swap_clients[0].ci(self.test_coin)
|
||||||
|
|
||||||
@@ -806,7 +804,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
assert len(tx_wallet["blockhash"]) == 64
|
assert len(tx_wallet["blockhash"]) == 64
|
||||||
|
|
||||||
def test_012_p2sh_p2wsh(self):
|
def test_012_p2sh_p2wsh(self):
|
||||||
logging.info("---------- Test {} p2sh-p2wsh".format(self.test_coin.name))
|
logging.info(f"---------- Test {self.test_coin.name} p2sh-p2wsh")
|
||||||
logging.info("Skipped")
|
logging.info("Skipped")
|
||||||
|
|
||||||
def test_01_a_full_swap(self):
|
def test_01_a_full_swap(self):
|
||||||
@@ -877,7 +875,7 @@ class TestBCH(BasicSwapTest):
|
|||||||
|
|
||||||
def test_06_preselect_inputs(self):
|
def test_06_preselect_inputs(self):
|
||||||
tla_from = self.test_coin.name
|
tla_from = self.test_coin.name
|
||||||
logging.info("---------- Test {} Preselected inputs".format(tla_from))
|
logging.info(f"---------- Test {tla_from} Preselected inputs")
|
||||||
logging.info("Skipped")
|
logging.info("Skipped")
|
||||||
|
|
||||||
def test_07_expire_stuck_accepted(self):
|
def test_07_expire_stuck_accepted(self):
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class TestFunctions(BaseTest):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepareExtraCoins(cls):
|
def prepareExtraCoins(cls):
|
||||||
|
# Save sent messages so tests can count them
|
||||||
for sc in cls.swap_clients:
|
for sc in cls.swap_clients:
|
||||||
sc._smsg_add_to_outbox = True
|
sc._smsg_add_to_outbox = True
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ class TestFunctions(BaseTest):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def do_test_01_full_swap(self, coin_from: Coins, coin_to: Coins) -> None:
|
def do_test_01_full_swap(self, coin_from: Coins, coin_to: Coins) -> None:
|
||||||
logging.info("---------- Test {} to {}".format(coin_from.name, coin_to.name))
|
logging.info(f"---------- Test {coin_from.name} to {coin_to.name}")
|
||||||
|
|
||||||
# Offerer sends the offer
|
# Offerer sends the offer
|
||||||
# Bidder sends the bid
|
# Bidder sends the bid
|
||||||
@@ -306,9 +307,7 @@ class TestFunctions(BaseTest):
|
|||||||
self, coin_from: Coins, coin_to: Coins, lock_value: int = 32
|
self, coin_from: Coins, coin_to: Coins, lock_value: int = 32
|
||||||
) -> None:
|
) -> None:
|
||||||
logging.info(
|
logging.info(
|
||||||
"---------- Test {} to {} leader recovers coin a lock tx".format(
|
f"---------- Test {coin_from.name} to {coin_to.name} leader recovers coin a lock tx"
|
||||||
coin_from.name, coin_to.name
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
id_offerer: int = self.node_a_id
|
id_offerer: int = self.node_a_id
|
||||||
@@ -459,6 +458,12 @@ class TestFunctions(BaseTest):
|
|||||||
if with_mercy
|
if with_mercy
|
||||||
else (BidStates.BID_STALLED_FOR_TEST, BidStates.XMR_SWAP_FAILED_SWIPED)
|
else (BidStates.BID_STALLED_FOR_TEST, BidStates.XMR_SWAP_FAILED_SWIPED)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
chain_a_coin = coin_to if reverse_bid else coin_from
|
||||||
|
if with_mercy is False and chain_a_coin == Coins.BCH:
|
||||||
|
# When using BCH, can't set XMR_SWAP_FAILED_SWIPED as should wait for mercy tx
|
||||||
|
expect_state = expect_state + (BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND,)
|
||||||
|
|
||||||
wait_for_bid(
|
wait_for_bid(
|
||||||
test_delay_event,
|
test_delay_event,
|
||||||
swap_clients[id_leader],
|
swap_clients[id_leader],
|
||||||
@@ -492,12 +497,12 @@ class TestFunctions(BaseTest):
|
|||||||
# Test manually redeeming the no-script lock tx
|
# Test manually redeeming the no-script lock tx
|
||||||
offerer_key = read_json_api(
|
offerer_key = read_json_api(
|
||||||
1800 + id_offerer,
|
1800 + id_offerer,
|
||||||
"bids/{}".format(bid_id.hex()),
|
f"bids/{bid_id.hex()}",
|
||||||
{"chainbkeysplit": True},
|
{"chainbkeysplit": True},
|
||||||
)["splitkey"]
|
)["splitkey"]
|
||||||
data = {"spendchainblocktx": True, "remote_key": offerer_key}
|
data = {"spendchainblocktx": True, "remote_key": offerer_key}
|
||||||
redeemed_txid = read_json_api(
|
redeemed_txid = read_json_api(
|
||||||
1800 + id_bidder, "bids/{}".format(bid_id.hex()), data
|
1800 + id_bidder, f"bids/{bid_id.hex()}", data
|
||||||
)["txid"]
|
)["txid"]
|
||||||
assert len(redeemed_txid) == 64
|
assert len(redeemed_txid) == 64
|
||||||
|
|
||||||
@@ -505,9 +510,7 @@ class TestFunctions(BaseTest):
|
|||||||
self, coin_from, coin_to, lock_value: int = 32
|
self, coin_from, coin_to, lock_value: int = 32
|
||||||
):
|
):
|
||||||
logging.info(
|
logging.info(
|
||||||
"---------- Test {} to {} follower recovers coin b lock tx".format(
|
f"---------- Test {coin_from.name} to {coin_to.name} follower recovers coin b lock tx"
|
||||||
coin_from.name, coin_to.name
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
id_offerer: int = self.node_a_id
|
id_offerer: int = self.node_a_id
|
||||||
@@ -920,7 +923,7 @@ class BasicSwapTest(TestFunctions):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(BasicSwapTest, cls).setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def addCoinSettings(cls, settings, datadir, node_id):
|
def addCoinSettings(cls, settings, datadir, node_id):
|
||||||
@@ -2480,11 +2483,6 @@ class BasicSwapTest(TestFunctions):
|
|||||||
def test_09_expire_accepted_rev(self):
|
def test_09_expire_accepted_rev(self):
|
||||||
self.do_test_09_expire_accepted(Coins.XMR, self.test_coin_from)
|
self.do_test_09_expire_accepted(Coins.XMR, self.test_coin_from)
|
||||||
|
|
||||||
def test_10_presigned_txns(self):
|
|
||||||
raise RuntimeError(
|
|
||||||
"TODO"
|
|
||||||
) # Build without xmr first for quicker test iterations
|
|
||||||
|
|
||||||
def test_11_fee_validation(self):
|
def test_11_fee_validation(self):
|
||||||
coin_from, coin_to = (self.test_coin_from, Coins.XMR)
|
coin_from, coin_to = (self.test_coin_from, Coins.XMR)
|
||||||
logging.info(
|
logging.info(
|
||||||
@@ -2822,7 +2820,7 @@ class TestBTC_PARTB(TestFunctions):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(TestBTC_PARTB, cls).setUpClass()
|
super().setUpClass()
|
||||||
if False:
|
if False:
|
||||||
for client in cls.swap_clients:
|
for client in cls.swap_clients:
|
||||||
client.log.safe_logs = True
|
client.log.safe_logs = True
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ def modify_config(test_path, i):
|
|||||||
with open(config_path, "w") as fp:
|
with open(config_path, "w") as fp:
|
||||||
json.dump(settings, fp, indent=4)
|
json.dump(settings, fp, indent=4)
|
||||||
|
|
||||||
|
btc_config_path = os.path.join(test_path, f"client{i}", "bitcoin", "bitcoin.conf")
|
||||||
|
with open(btc_config_path, "a") as fp:
|
||||||
|
fp.write("minrelaytxfee=0.00001\n")
|
||||||
|
|
||||||
|
|
||||||
def wait_for_bid_state(
|
def wait_for_bid_state(
|
||||||
delay_event, node_port: int, bid_id: str, state=None, wait_for: int = 30
|
delay_event, node_port: int, bid_id: str, state=None, wait_for: int = 30
|
||||||
@@ -641,7 +645,7 @@ class Test(TestFunctions):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.addElectrumxDaemon("bitcoin", 32793, 50001)
|
cls.addElectrumxDaemon("bitcoin", 32793, 50001)
|
||||||
super(Test, cls).setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def modifyConfig(cls, test_path, i):
|
def modifyConfig(cls, test_path, i):
|
||||||
@@ -754,14 +758,6 @@ class Test(TestFunctions):
|
|||||||
self.delay_event,
|
self.delay_event,
|
||||||
self.test_coin_b,
|
self.test_coin_b,
|
||||||
100,
|
100,
|
||||||
self.port_node_1,
|
|
||||||
self.port_node_0,
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
prepare_balance(
|
|
||||||
self.delay_event,
|
|
||||||
self.test_coin_xmr,
|
|
||||||
100,
|
|
||||||
self.port_node_0,
|
self.port_node_0,
|
||||||
self.port_node_1,
|
self.port_node_1,
|
||||||
True,
|
True,
|
||||||
@@ -788,7 +784,7 @@ class Test(TestFunctions):
|
|||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
self.do_test_03_follower_recover_a_lock_tx(
|
self.do_test_03_follower_recover_a_lock_tx(
|
||||||
self.test_coin_b, self.test_coin_xmr, self.port_node_1, self.port_node_0
|
self.test_coin_b, self.test_coin_xmr, self.port_node_0, self.port_node_1
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_03_b_follower_recover_a_lock_tx_reverse(self):
|
def test_03_b_follower_recover_a_lock_tx_reverse(self):
|
||||||
|
|||||||
@@ -48,15 +48,11 @@ class TestLTC(BasicSwapTest):
|
|||||||
assert deploymentinfo["softforks"][feature_name]["active"] is True
|
assert deploymentinfo["softforks"][feature_name]["active"] is True
|
||||||
|
|
||||||
def test_001_nested_segwit(self):
|
def test_001_nested_segwit(self):
|
||||||
logging.info(
|
logging.info(f"---------- Test {self.test_coin_from.name} p2sh nested segwit")
|
||||||
"---------- Test {} p2sh nested segwit".format(self.test_coin_from.name)
|
|
||||||
)
|
|
||||||
logging.info("Skipped")
|
logging.info("Skipped")
|
||||||
|
|
||||||
def test_002_native_segwit(self):
|
def test_002_native_segwit(self):
|
||||||
logging.info(
|
logging.info(f"---------- Test {self.test_coin_from.name} p2sh native segwit")
|
||||||
"---------- Test {} p2sh native segwit".format(self.test_coin_from.name)
|
|
||||||
)
|
|
||||||
|
|
||||||
ci = self.swap_clients[0].ci(self.test_coin_from)
|
ci = self.swap_clients[0].ci(self.test_coin_from)
|
||||||
addr_segwit = ci.rpc_wallet("getnewaddress", ["segwit test", "bech32"])
|
addr_segwit = ci.rpc_wallet("getnewaddress", ["segwit test", "bech32"])
|
||||||
@@ -120,7 +116,7 @@ class TestLTC(BasicSwapTest):
|
|||||||
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
assert tx_funded_decoded["txid"] == tx_signed_decoded["txid"]
|
||||||
|
|
||||||
def test_007_hdwallet(self):
|
def test_007_hdwallet(self):
|
||||||
logging.info("---------- Test {} hdwallet".format(self.test_coin_from.name))
|
logging.info(f"---------- Test {self.test_coin_from.name} hdwallet")
|
||||||
|
|
||||||
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
|
||||||
test_wif = (
|
test_wif = (
|
||||||
@@ -136,7 +132,7 @@ class TestLTC(BasicSwapTest):
|
|||||||
assert addr == "rltc1qps7hnjd866e9ynxadgseprkc2l56m00djr82la"
|
assert addr == "rltc1qps7hnjd866e9ynxadgseprkc2l56m00djr82la"
|
||||||
|
|
||||||
def test_20_btc_coin(self):
|
def test_20_btc_coin(self):
|
||||||
logging.info("---------- Test BTC to {}".format(self.test_coin_from.name))
|
logging.info(f"---------- Test BTC to {self.test_coin_from.name}")
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
|
|
||||||
offer_id = swap_clients[0].postOffer(
|
offer_id = swap_clients[0].postOffer(
|
||||||
@@ -178,7 +174,7 @@ class TestLTC(BasicSwapTest):
|
|||||||
assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0
|
assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0
|
||||||
|
|
||||||
def test_21_mweb(self):
|
def test_21_mweb(self):
|
||||||
logging.info("---------- Test MWEB {}".format(self.test_coin_from.name))
|
logging.info(f"---------- Test MWEB {self.test_coin_from.name}")
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
|
|
||||||
ci0 = swap_clients[0].ci(self.test_coin_from)
|
ci0 = swap_clients[0].ci(self.test_coin_from)
|
||||||
@@ -327,7 +323,7 @@ class TestLTC(BasicSwapTest):
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
def test_22_mweb_balance(self):
|
def test_22_mweb_balance(self):
|
||||||
logging.info("---------- Test MWEB balance {}".format(self.test_coin_from.name))
|
logging.info(f"---------- Test MWEB balance {self.test_coin_from.name}")
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
|
|
||||||
ci_mweb = swap_clients[0].ci(Coins.LTC_MWEB)
|
ci_mweb = swap_clients[0].ci(Coins.LTC_MWEB)
|
||||||
|
|||||||
@@ -815,7 +815,7 @@ class BaseTest(unittest.TestCase):
|
|||||||
.pubkey_to_address(void_block_rewards_pubkey)
|
.pubkey_to_address(void_block_rewards_pubkey)
|
||||||
)
|
)
|
||||||
logging.info(
|
logging.info(
|
||||||
"Mining %d Litecoin blocks to %s", num_blocks, cls.ltc_addr
|
f"Mining {num_blocks} Litecoin blocks to {cls.ltc_addr}"
|
||||||
)
|
)
|
||||||
callnoderpc(
|
callnoderpc(
|
||||||
0,
|
0,
|
||||||
@@ -942,6 +942,7 @@ class BaseTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
cls.coins_update_thread.start()
|
cls.coins_update_thread.start()
|
||||||
|
|
||||||
|
cls.prepareBalances()
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
cls.tearDownClass()
|
cls.tearDownClass()
|
||||||
@@ -999,6 +1000,10 @@ class BaseTest(unittest.TestCase):
|
|||||||
def prepareExtraCoins(cls):
|
def prepareExtraCoins(cls):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def prepareBalances(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coins_loop(cls):
|
def coins_loop(cls):
|
||||||
if cls.btc_addr is not None:
|
if cls.btc_addr is not None:
|
||||||
|
|||||||
+18
-2
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022-2024 tecnovert
|
# Copyright (c) 2022-2024 tecnovert
|
||||||
@@ -7,9 +6,14 @@
|
|||||||
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
import urllib
|
import urllib
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
PORT_OFS = int(os.getenv("PORT_OFS", 1))
|
||||||
|
UI_PORT = 12700 + PORT_OFS
|
||||||
|
|
||||||
REQUIRED_SETTINGS = {
|
REQUIRED_SETTINGS = {
|
||||||
"blocks_confirmed": 1,
|
"blocks_confirmed": 1,
|
||||||
"conf_target": 1,
|
"conf_target": 1,
|
||||||
@@ -67,5 +71,17 @@ def waitForServer(delay_event, port, wait_for=40):
|
|||||||
_ = read_json_api(port)
|
_ = read_json_api(port)
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("waitForServer, error:", str(e))
|
logging.error(f"waitForServer: {e}")
|
||||||
raise ValueError("waitForServer failed")
|
raise ValueError("waitForServer failed")
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_offers(delay_event, node_id, num_offers, offer_id=None) -> None:
|
||||||
|
logging.info(f"Waiting for {num_offers} offers on node {node_id}")
|
||||||
|
for i in range(20):
|
||||||
|
delay_event.wait(1)
|
||||||
|
offers = read_json_api(
|
||||||
|
UI_PORT + node_id, "offers" if offer_id is None else f"offers/{offer_id}"
|
||||||
|
)
|
||||||
|
if len(offers) >= num_offers:
|
||||||
|
return
|
||||||
|
raise ValueError("wait_for_offers failed")
|
||||||
|
|||||||
Reference in New Issue
Block a user