Fix recoverNoScriptTxnWithKey regression, add to more tests.

This commit is contained in:
tecnovert 2025-01-06 20:15:37 +02:00
parent a53de511ce
commit 28d99c4c0f
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
10 changed files with 97 additions and 59 deletions

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019-2024 tecnovert # Copyright (c) 2019-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -10327,7 +10327,14 @@ class BasicSwap(BaseApp):
elif coin == Coins.NAV: elif coin == Coins.NAV:
rv["immature"] = walletinfo["immature_balance"] rv["immature"] = walletinfo["immature_balance"]
elif coin == Coins.LTC: elif coin == Coins.LTC:
rv["mweb_address"] = self.getCachedStealthAddressForCoin(Coins.LTC_MWEB) try:
rv["mweb_address"] = self.getCachedStealthAddressForCoin(
Coins.LTC_MWEB
)
except Exception as e:
self.log.warning(
f"getCachedStealthAddressForCoin for {ci.coin_name()} failed with: {e}"
)
rv["mweb_balance"] = walletinfo["mweb_balance"] rv["mweb_balance"] = walletinfo["mweb_balance"]
rv["mweb_pending"] = ( rv["mweb_pending"] = (
walletinfo["mweb_unconfirmed"] + walletinfo["mweb_immature"] walletinfo["mweb_unconfirmed"] + walletinfo["mweb_immature"]

View file

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2024 tecnovert # Copyright (c) 2024 tecnovert
# Copyright (c) 2025 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.
@ -53,6 +54,10 @@ class CoinInterface:
self._mx_wallet = threading.Lock() self._mx_wallet = threading.Lock()
self._altruistic = True self._altruistic = True
def interface_type(self) -> int:
# coin_type() returns the base coin type, interface_type() returns the coin+balance type.
return self.coin_type()
def setDefaults(self): def setDefaults(self):
self._unknown_wallet_seed = True self._unknown_wallet_seed = True
self._restore_height = None self._restore_height = None

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2024 tecnovert # Copyright (c) 2020-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -1397,6 +1397,7 @@ class BTCInterface(Secp256k1Interface):
cb_swap_value: int, cb_swap_value: int,
b_fee: int, b_fee: int,
restore_height: int, restore_height: int,
spend_actual_balance: bool = False,
lock_tx_vout=None, lock_tx_vout=None,
) -> bytes: ) -> bytes:
self._log.info( self._log.info(

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2024 tecnovert # Copyright (c) 2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -1726,6 +1726,7 @@ class DCRInterface(Secp256k1Interface):
cb_swap_value: int, cb_swap_value: int,
b_fee: int, b_fee: int,
restore_height: int, restore_height: int,
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("spendBLockTx %s:\n", chain_b_lock_txid.hex())

View file

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2023 tecnovert # Copyright (c) 2020-2023 tecnovert
# Copyright (c) 2024-2025 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.
@ -52,7 +53,6 @@ class LTCInterface(BTCInterface):
def getWalletInfo(self): def getWalletInfo(self):
rv = super(LTCInterface, self).getWalletInfo() rv = super(LTCInterface, self).getWalletInfo()
mweb_info = self.rpc_wallet_mweb("getwalletinfo") mweb_info = self.rpc_wallet_mweb("getwalletinfo")
rv["mweb_balance"] = mweb_info["balance"] rv["mweb_balance"] = mweb_info["balance"]
rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"] rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"]
@ -88,8 +88,8 @@ class LTCInterface(BTCInterface):
class LTCInterfaceMWEB(LTCInterface): class LTCInterfaceMWEB(LTCInterface):
@staticmethod
def coin_type(): def interface_type(self) -> int:
return Coins.LTC_MWEB return Coins.LTC_MWEB
def __init__(self, coin_settings, network, swap_client=None): def __init__(self, coin_settings, network, swap_client=None):

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2023 tecnovert # Copyright (c) 2023 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -666,6 +666,7 @@ class NAVInterface(BTCInterface):
cb_swap_value: int, cb_swap_value: int,
b_fee: int, b_fee: int,
restore_height: int, restore_height: int,
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("spendBLockTx %s:\n", chain_b_lock_txid.hex())

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2024 tecnovert # Copyright (c) 2020-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -187,6 +187,10 @@ class PARTInterface(BTCInterface):
class PARTInterfaceBlind(PARTInterface): class PARTInterfaceBlind(PARTInterface):
def interface_type(self) -> int:
return Coins.PART_BLIND
@staticmethod @staticmethod
def balance_type(): def balance_type():
return BalanceTypes.BLIND return BalanceTypes.BLIND
@ -1174,6 +1178,10 @@ class PARTInterfaceBlind(PARTInterface):
class PARTInterfaceAnon(PARTInterface): class PARTInterfaceAnon(PARTInterface):
def interface_type(self) -> int:
return Coins.PART_ANON
@staticmethod @staticmethod
def balance_type(): def balance_type():
return BalanceTypes.ANON return BalanceTypes.ANON

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2024 tecnovert # Copyright (c) 2020-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -15,9 +15,10 @@ from basicswap.chainparams import (
Coins, Coins,
) )
from basicswap.basicswap_util import ( from basicswap.basicswap_util import (
EventLogTypes,
KeyTypes, KeyTypes,
SwapTypes, SwapTypes,
EventLogTypes, TxTypes,
) )
from . import ProtocolInterface from . import ProtocolInterface
from basicswap.contrib.test_framework.script import CScript, CScriptOp, OP_CHECKMULTISIG from basicswap.contrib.test_framework.script import CScript, CScriptOp, OP_CHECKMULTISIG
@ -55,7 +56,7 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex())) ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
# 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) reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
ci_from = self.ci(Coins(offer.coin_from)) ci_from = self.ci(Coins(offer.coin_from))
ci_to = self.ci(Coins(offer.coin_to)) ci_to = self.ci(Coins(offer.coin_to))
ci_follower = ci_from if reverse_bid else ci_to ci_follower = ci_from if reverse_bid else ci_to
@ -89,16 +90,20 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
summed_pkbs = ci_follower.getPubkey(vkbs) summed_pkbs = ci_follower.getPubkey(vkbs)
if summed_pkbs != xmr_swap.pkbs: if summed_pkbs != xmr_swap.pkbs:
err_msg: str = "Summed key does not match expected wallet spend pubkey" err_msg: str = "Summed key does not match expected wallet spend pubkey"
have_pk = summed_pkbs.hex() self.log.error(
expect_pk = xmr_swap.pkbs.hex() f"{err_msg}. Got: {summed_pkbs.hex()}, Expect: {xmr_swap.pkbs.hex()}"
self.log.error(f"{err_msg}. Got: {have_pk}, Expect: {expect_pk}") )
raise ValueError(err_msg) raise ValueError(err_msg)
if ci_follower.coin_type() in (Coins.XMR, Coins.WOW): coin_to: int = ci_follower.interface_type()
base_coin_to: int = ci_follower.coin_type()
if coin_to in (Coins.XMR, Coins.WOW):
address_to = self.getCachedMainWalletAddress(ci_follower, use_cursor) address_to = self.getCachedMainWalletAddress(ci_follower, use_cursor)
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
address_to = self.getCachedStealthAddressForCoin(base_coin_to, use_cursor)
else: else:
address_to = self.getCachedStealthAddressForCoin( address_to = self.getReceiveAddressFromPool(
ci_follower.coin_type(), use_cursor base_coin_to, bid_id, TxTypes.XMR_SWAP_B_LOCK_SPEND, use_cursor
) )
amount = bid.amount_to amount = bid.amount_to
lock_tx_vout = bid.getLockTXBVout() lock_tx_vout = bid.getLockTXBVout()
@ -145,10 +150,11 @@ def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
return ci_follower.encodeKey( return ci_follower.encodeKey(
swap_client.getPathKey( swap_client.getPathKey(
ci_leader.coin_type(), ci_leader.interface_type(),
ci_follower.coin_type(), ci_follower.interface_type(),
bid.created_at, bid.created_at,
xmr_swap.contract_count, xmr_swap.contract_count,
key_type, key_type,

View file

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2021-2024 tecnovert # Copyright (c) 2021-2024 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 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.
@ -170,7 +170,11 @@ class TestFunctions(BaseTest):
bid_id = swap_clients[id_bidder].postXmrBid(offer_id, offer.amount_from) bid_id = swap_clients[id_bidder].postXmrBid(offer_id, offer.amount_from)
wait_for_bid( wait_for_bid(
test_delay_event, swap_clients[id_offerer], bid_id, BidStates.BID_RECEIVED test_delay_event,
swap_clients[id_offerer],
bid_id,
BidStates.BID_RECEIVED,
wait_for=(self.extra_wait_time + 40),
) )
bid0 = read_json_api(1800 + id_offerer, f"bids/{bid_id.hex()}") bid0 = read_json_api(1800 + id_offerer, f"bids/{bid_id.hex()}")
@ -392,7 +396,7 @@ class TestFunctions(BaseTest):
) )
swap_clients[id_follower].ci( swap_clients[id_follower].ci(
coin_from if reverse_bid else coin_to coin_to if reverse_bid else coin_from
)._altruistic = with_mercy )._altruistic = with_mercy
amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1) amt_swap = ci_from.make_int(random.uniform(0.1, 2.0), r=1)
@ -415,17 +419,10 @@ class TestFunctions(BaseTest):
test_delay_event, swap_clients[id_offerer], bid_id, BidStates.BID_RECEIVED test_delay_event, swap_clients[id_offerer], bid_id, BidStates.BID_RECEIVED
) )
debug_type = ( swap_clients[id_leader].setBidDebugInd(
DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND2 bid_id, DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND2
if with_mercy
else DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND
)
swap_clients[id_leader].setBidDebugInd(bid_id, debug_type)
debug_type = (
DebugTypes.BID_DONT_SPEND_COIN_B_LOCK
if with_mercy
else DebugTypes.BID_STOP_AFTER_COIN_A_LOCK
) )
debug_type = DebugTypes.BID_DONT_SPEND_COIN_B_LOCK
swap_clients[id_follower].setBidDebugInd(bid_id, debug_type) swap_clients[id_follower].setBidDebugInd(bid_id, debug_type)
swap_clients[id_leader].setBidDebugInd( swap_clients[id_leader].setBidDebugInd(
@ -442,7 +439,7 @@ class TestFunctions(BaseTest):
expect_state = ( expect_state = (
(BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED, BidStates.SWAP_COMPLETED) (BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED, BidStates.SWAP_COMPLETED)
if with_mercy if with_mercy
else BidStates.BID_STALLED_FOR_TEST else (BidStates.BID_STALLED_FOR_TEST, BidStates.XMR_SWAP_FAILED_SWIPED)
) )
wait_for_bid( wait_for_bid(
test_delay_event, test_delay_event,
@ -473,6 +470,19 @@ class TestFunctions(BaseTest):
wait_for_none_active(test_delay_event, 1800 + id_offerer) wait_for_none_active(test_delay_event, 1800 + id_offerer)
wait_for_none_active(test_delay_event, 1800 + id_bidder) wait_for_none_active(test_delay_event, 1800 + id_bidder)
if with_mercy is False:
# Test manually redeeming the no-script lock tx
offerer_key = read_json_api(
1800 + id_offerer,
"bids/{}".format(bid_id.hex()),
{"chainbkeysplit": True},
)["splitkey"]
data = {"spendchainblocktx": True, "remote_key": offerer_key}
redeemed_txid = read_json_api(
1800 + id_bidder, "bids/{}".format(bid_id.hex()), data
)["txid"]
assert len(redeemed_txid) == 64
def do_test_04_follower_recover_b_lock_tx( def do_test_04_follower_recover_b_lock_tx(
self, coin_from, coin_to, lock_value: int = 32 self, coin_from, coin_to, lock_value: int = 32
): ):
@ -1601,7 +1611,13 @@ class BasicSwapTest(TestFunctions):
offer = swap_clients[1].getOffer(offer_id) offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[2], bid_id, BidStates.BID_RECEIVED) wait_for_bid(
test_delay_event,
swap_clients[2],
bid_id,
BidStates.BID_RECEIVED,
wait_for=(self.extra_wait_time + 40),
)
swap_clients[2].acceptBid(bid_id) swap_clients[2].acceptBid(bid_id)
wait_for_bid( wait_for_bid(
@ -1662,7 +1678,13 @@ class BasicSwapTest(TestFunctions):
wait_for_offer(test_delay_event, swap_clients[1], offer_id) wait_for_offer(test_delay_event, swap_clients[1], offer_id)
bid_id = swap_clients[1].postXmrBid(offer_id, amt_swap) bid_id = swap_clients[1].postXmrBid(offer_id, amt_swap)
swap_clients[1].abandonBid(bid_id) swap_clients[1].abandonBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_ACCEPTED) wait_for_bid(
test_delay_event,
swap_clients[0],
bid_id,
BidStates.BID_ACCEPTED,
wait_for=(self.extra_wait_time + 40),
)
try: try:
swap_clients[0].setMockTimeOffset(7200) swap_clients[0].setMockTimeOffset(7200)

View file

@ -2,16 +2,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2021-2023 tecnovert # Copyright (c) 2021-2023 tecnovert
# Copyright (c) 2024 The Basicswap developers # Copyright (c) 2024-2025 The Basicswap developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import json
import random import random
import logging import logging
import unittest import unittest
from urllib import parse
from urllib.request import urlopen
from basicswap.basicswap import ( from basicswap.basicswap import (
Coins, Coins,
@ -27,7 +24,6 @@ from basicswap.util import (
format_amount, format_amount,
) )
from tests.basicswap.util import ( from tests.basicswap.util import (
post_json_req,
read_json_api, read_json_api,
) )
from tests.basicswap.common import ( from tests.basicswap.common import (
@ -61,9 +57,7 @@ class Test(BaseTest):
"subfee": False, "subfee": False,
"type_to": "blind", "type_to": "blind",
} }
json_rv = json.loads( json_rv = read_json_api(1800, "wallets/part/withdraw", post_json)
post_json_req("http://127.0.0.1:1800/json/wallets/part/withdraw", post_json)
)
assert len(json_rv["txid"]) == 64 assert len(json_rv["txid"]) == 64
logging.info("Waiting for blind balance") logging.info("Waiting for blind balance")
@ -388,7 +382,7 @@ class Test(BaseTest):
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK) swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
swap_clients[0].setBidDebugInd( swap_clients[0].setBidDebugInd(
bid_id, DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND bid_id, DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND2
) )
swap_clients[0].acceptXmrBid(bid_id) swap_clients[0].acceptXmrBid(bid_id)
@ -397,7 +391,7 @@ class Test(BaseTest):
test_delay_event, test_delay_event,
swap_clients[0], swap_clients[0],
bid_id, bid_id,
BidStates.BID_STALLED_FOR_TEST, (BidStates.BID_STALLED_FOR_TEST, BidStates.XMR_SWAP_FAILED_SWIPED),
wait_for=180, wait_for=180,
) )
wait_for_bid( wait_for_bid(
@ -422,21 +416,14 @@ class Test(BaseTest):
wait_for_none_active(test_delay_event, 1800) wait_for_none_active(test_delay_event, 1800)
wait_for_none_active(test_delay_event, 1801) wait_for_none_active(test_delay_event, 1801)
data = parse.urlencode({"chainbkeysplit": True}).encode() offerer_key = read_json_api(
offerer_key = json.loads( 1800, "bids/{}".format(bid_id.hex()), {"chainbkeysplit": True}
urlopen(
"http://127.0.0.1:1800/json/bids/{}".format(bid_id.hex()), data=data
).read()
)["splitkey"] )["splitkey"]
data = parse.urlencode( data = {"spendchainblocktx": True, "remote_key": offerer_key}
{"spendchainblocktx": True, "remote_key": offerer_key} redeemed_txid = read_json_api(1801, "bids/{}".format(bid_id.hex()), data)[
).encode() "txid"
redeemed_txid = json.loads( ]
urlopen(
"http://127.0.0.1:1801/json/bids/{}".format(bid_id.hex()), data=data
).read()
)["txid"]
assert len(redeemed_txid) == 64 assert len(redeemed_txid) == 64
def do_test_04_follower_recover_b_lock_tx(self, coin_from, coin_to): def do_test_04_follower_recover_b_lock_tx(self, coin_from, coin_to):